string.tin 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. #nop vim: set filetype=tt:;
  2. /*
  3. 本文件属于 PaoTin++ 的一部分。
  4. PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp <danzipao@gmail.com>) 享有并保留一切法律权利
  5. 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。
  6. */
  7. #nop 本文件是 xtintin 的一部分,实现了一些字符串处理函数;
  8. ///=== {
  9. ///// 字符串处理函数:
  10. //
  11. // #@ str.Len <字符串>
  12. // 给出指定字符串的长度。
  13. // 本函数也可以简写为 len,两个名称用途和用法完全相同。
  14. //
  15. // #@ str.Width <字符串>
  16. // 给出指定字符串的屏幕显示宽度(忽略颜色等控制字符,每个汉字按两个字符宽度计算。
  17. // 本函数也可以简写为 width,两个名称用途和用法完全相同。
  18. // };
  19. #func {str.Len} {#format result {%L} {%0}};
  20. #func {len} {#return {@str.Len{%0}}};
  21. #func {str.Width} {#format result {%W} {%0}};
  22. #func {width} {#return {@str.Width{%0}}};
  23. ///=== {
  24. // #@ str.Space <长度>
  25. // 生成指定长度的空白字符串。
  26. // 本函数也可以简写为 space,两个名称用途和用法完全相同。
  27. //
  28. // #@ str.Repeat <次数> <字符串>
  29. // 将指定的字符串重复若干次。
  30. // 本函数也可以简写为 repeat,两个名称用途和用法完全相同。
  31. //
  32. // #@ str.Reverse <字符串>
  33. // 将字符串按照倒序重排每个字符并返回新的字符串。
  34. // };
  35. #func {str.Space} {#return {@str.Repeat{{%0};{ }}}};
  36. #func {space} {#return {@str.Space{%0}}};
  37. #func {str.Repeat} {#var result {}; #loop 1 {%1} tmp {#cat result {%2}}};
  38. #func {repeat} {#return {@str.Repeat{%0}}};
  39. #func {str.Reverse} {#var result {}; #parse {%0} {tmp} {#var result {$tmp$result}}};
  40. ///=== {
  41. // #@ str.Trim <字符串>
  42. // 去除给定字符串首尾的连续空白。
  43. // 本函数也可以简写为 trim,两个名称用途和用法完全相同。
  44. //
  45. // #@ str.TrimAll <字符串>
  46. // 去除字符串中全部的空白。
  47. // 本函数也可以简写为 trimAll,两个名称用途和用法完全相同。
  48. //
  49. // #@ str.Plain <字符串>
  50. // 去除字符串中的颜色代码和 ANSI 控制字符。
  51. //
  52. // #@ str.Color <字符串>
  53. // 仅保留字符串中的颜色代码,去掉其余成分,方便比较颜色。
  54. // };
  55. #func {str.Trim} {#format result {%p} {%0}};
  56. #func {trim} {#return {@str.Trim{%0}}};
  57. #func {str.TrimAll} {#var result {%0}; #replace {result} {%+1..s} {}};
  58. #func {trimAll} {#return {@str.TrimAll{%0}}};
  59. #func {str.Plain} {#format result {%P} {%0}};
  60. #func {str.Color} {
  61. #local str {};
  62. #local state {START};
  63. #local ch {};
  64. #parse {%0} {ch} {
  65. #switch {"$state/$ch"} {
  66. #case {"START/\e"} {#local state {ESCAPE}};
  67. #case {"START/%*"} {#0};
  68. #case {"ESCAPE/["} {#local state {INNER}};
  69. #case {"ESCAPE/%*"} {#local state {OUTER}};
  70. #case {"INNER/m"} {#cat str {;}; #local state {OUTER}};
  71. #case {"INNER/%*"} {#cat str {$ch}};
  72. #case {"OUTER/\e"} {#local state {ESCAPE}};
  73. };
  74. };
  75. #replace {str} {;$} {};
  76. #return {$str};
  77. };
  78. ///=== {
  79. // #@ str.Replace <字符串> <正则表达式> <替换表达式>
  80. // 在给定字符串中执行替换,凡是被正则表达式匹配的东西都会用替换表达式进行替换。
  81. //
  82. // #@ str.ToLower <字符串>
  83. // 将给定字符串转换成小写。
  84. // 本函数也可以简写为 toLower,两个名称用途和用法完全相同。
  85. //
  86. // #@ str.ToUpper <字符串>
  87. // 将给定字符串转换成大写。
  88. // 本函数也可以简写为 toUpper,两个名称用途和用法完全相同。
  89. //
  90. // #@ str.Capital <字符串>
  91. // 将给定字符串转换成首字母大写。
  92. // };
  93. #func {str.Replace} {#var result {%1}; #replace result {%2} {%3}};
  94. #func {str.ToLower} {#format result {%l} {%0}};
  95. #func {toLower} {#return {@str.ToLower{%0}}};
  96. #func {str.ToUpper} {#format result {%u} {%0}};
  97. #func {toUpper} {#return {@str.ToUpper{%0}}};
  98. #func {str.Capital} {#format result {%n} {%0}};
  99. ///=== {
  100. // #@ str.FromCode <十进制代码>
  101. // 将十进制代码转换成对应的 ASCII 或者 Unicode 字符。
  102. // 本函数也可以简写为 char,两个名称用途和用法完全相同。
  103. //
  104. // #@ str.ToCode <字符>
  105. // 获得该字符在 Unicode 编码中的码点,以十进制整数形式表示。
  106. // 本函数也可以简写为 codepoint,两个名称用途和用法完全相同。
  107. //
  108. // #@ str.FromHexCode <十六进制代码>
  109. // 将十六进制代码转换成对应的 ASCII 或者 Unicode 字符。
  110. // 本函数也可以简写为 hex2char,两个名称用途和用法完全相同。
  111. //
  112. // #@ str.ToHexCode <字符>
  113. // 获得该字符在 Unicode 编码中的码点,以十六进制整数形式表示。
  114. // 本函数也可以简写为 char2hex,两个名称用途和用法完全相同。
  115. //
  116. // #@ str.HexToDec <十六进制串转十进制串>
  117. // 数字字符串转换,十六进制串转十进制串。
  118. // 本函数也可以简写为 hex2dec,两个名称用途和用法完全相同。
  119. //
  120. // #@ str.DecToHex <十进制串转十六进制串>
  121. // 数字字符串转换,十进制串转十六进制串。
  122. // 本函数也可以简写为 dec2hex,两个名称用途和用法完全相同。
  123. // };
  124. #func {str.FromCode} {#format result {%X} {%0}; #format result {%x} {$result}};
  125. #func {char} {#return {@str.FromCode{%0}}};
  126. #func {str.ToCode} {#format result {%A} {%0}};
  127. #func {codepoint} {#return {@str.ToCode{%0}}};
  128. #func {str.FromHexCode} {#format result {%x} {%0}};
  129. #func {hex2char} {#return {@str.FromHexCode{%0}}};
  130. #func {str.ToHexCode} {#format result {%A} {%0}; #return @str.DecToHex{$result}};
  131. #func {char2hex} {#return {@str.ToHexCode{%0}}};
  132. #func {str.HexToDec} {#format result {%D} {%0}};
  133. #func {hex2dec} {#return {@str.HexToDec{%0}}};
  134. #func {str.DecToHex} {#format result {00%X} {%0}; #format result {%.-2s} {$result}};
  135. #func {dec2hex} {#return {@str.DecToHex{%0}}};
  136. ///=== {
  137. // #@ str.BinToHex <二进制数字串>
  138. // 二进制转十六进制
  139. //
  140. // EXAMPLE: \@str.BinToHex{0}
  141. // RESULT: {0}
  142. //
  143. // EXAMPLE: \@str.BinToHex{1}
  144. // RESULT: {1}
  145. //
  146. // EXAMPLE: \@str.BinToHex{10}
  147. // RESULT: {2}
  148. //
  149. // EXAMPLE: \@str.BinToHex{0111}
  150. // RESULT: {7}
  151. //
  152. // EXAMPLE: \@str.BinToHex{1000}
  153. // RESULT: {8}
  154. //
  155. // EXAMPLE: \@str.BinToHex{1111}
  156. // RESULT: {F}
  157. //
  158. // EXAMPLE: \@str.BinToHex{01011}
  159. // RESULT: {0B}
  160. //
  161. // EXAMPLE: \@str.BinToHex{0000010010001101001010101111001101}
  162. // RESULT: {01234ABCD}
  163. // };
  164. #func {str.BinToHex} {
  165. #local bin {%1};
  166. #local hex {};
  167. #if { "$bin" == "" } {
  168. #return {};
  169. };
  170. #local len {@str.Len{$bin}};
  171. #switch {$len % 4} {
  172. #case {0} {#0};
  173. #case {1} {#local bin {000$bin}};
  174. #case {2} {#local bin {00$bin}};
  175. #case {3} {#local bin {0$bin}};
  176. };
  177. #math len {(($len - 1) % 4 + 1) * 4};
  178. #while {1} {
  179. #if { @str.Len{$bin} < 1 } {
  180. #return {$hex};
  181. };
  182. #local parts {$bin};
  183. #replace parts {^%+4S%*$} {
  184. {left} {&1}
  185. {right} {&2}
  186. };
  187. #local parts {$parts};
  188. #switch {"$parts[left]"} {
  189. #case {"0000"} {#cat hex {0}};
  190. #case {"0001"} {#cat hex {1}};
  191. #case {"0010"} {#cat hex {2}};
  192. #case {"0011"} {#cat hex {3}};
  193. #case {"0100"} {#cat hex {4}};
  194. #case {"0101"} {#cat hex {5}};
  195. #case {"0110"} {#cat hex {6}};
  196. #case {"0111"} {#cat hex {7}};
  197. #case {"1000"} {#cat hex {8}};
  198. #case {"1001"} {#cat hex {9}};
  199. #case {"1010"} {#cat hex {A}};
  200. #case {"1011"} {#cat hex {B}};
  201. #case {"1100"} {#cat hex {C}};
  202. #case {"1101"} {#cat hex {D}};
  203. #case {"1110"} {#cat hex {E}};
  204. #case {"1111"} {#cat hex {F}};
  205. };
  206. #local bin {$parts[right]};
  207. };
  208. };
  209. ///=== {
  210. // #@ str.HexToBin <十六进制数字串>
  211. // 十六进制转二进制
  212. //
  213. // EXAMPLE: \@str.HexToBin{0}
  214. // RESULT: {0000}
  215. //
  216. // EXAMPLE: \@str.HexToBin{1}
  217. // RESULT: {0001}
  218. //
  219. // EXAMPLE: \@str.HexToBin{2}
  220. // RESULT: {0010}
  221. //
  222. // EXAMPLE: \@str.HexToBin{7}
  223. // RESULT: {0111}
  224. //
  225. // EXAMPLE: \@str.HexToBin{8}
  226. // RESULT: {1000}
  227. //
  228. // EXAMPLE: \@str.HexToBin{F}
  229. // RESULT: {1111}
  230. //
  231. // EXAMPLE: \@str.HexToBin{f}
  232. // RESULT: {1111}
  233. //
  234. // EXAMPLE: \@str.HexToBin{0B}
  235. // RESULT: {00001011}
  236. //
  237. // EXAMPLE: \@str.HexToBin{01234ABCD}
  238. // RESULT: {000000010010001101001010101111001101}
  239. // };
  240. #func {str.HexToBin} {
  241. #local hex {%1};
  242. #local bin {};
  243. #while {1} {
  244. #local parts {$hex};
  245. #if { @str.Len{$hex} < 1 } {
  246. #return {$bin};
  247. };
  248. #replace parts {^%+1S%*$} {
  249. {left} {&1}
  250. {right} {&2}
  251. };
  252. #local parts {$parts};
  253. #local hex {$parts[right]};
  254. #switch {"$parts[left]"} {
  255. #case {"0"} {#cat bin {0000}};
  256. #case {"1"} {#cat bin {0001}};
  257. #case {"2"} {#cat bin {0010}};
  258. #case {"3"} {#cat bin {0011}};
  259. #case {"4"} {#cat bin {0100}};
  260. #case {"5"} {#cat bin {0101}};
  261. #case {"6"} {#cat bin {0110}};
  262. #case {"7"} {#cat bin {0111}};
  263. #case {"8"} {#cat bin {1000}};
  264. #case {"9"} {#cat bin {1001}};
  265. #case {"{A|a}"} {#cat bin {1010}};
  266. #case {"{B|b}"} {#cat bin {1011}};
  267. #case {"{C|c}"} {#cat bin {1100}};
  268. #case {"{D|d}"} {#cat bin {1101}};
  269. #case {"{E|e}"} {#cat bin {1110}};
  270. #case {"{F|f}"} {#cat bin {1111}};
  271. };
  272. };
  273. };
  274. #func {bin2hex} {#return {@str.BinToHex{%0}}};
  275. #func {hex2bin} {#return {@str.HexToBin{%0}}};
  276. ///=== {
  277. // #@ str.Format <格式> <参数>
  278. // 执行 #format result <格式> <参数> 操作。
  279. // 本函数也可以简写为 format,两个名称用途和用法完全相同。
  280. // };
  281. #func {str.Format} {#format result {%1} {%2}};
  282. #func {format} {#return {@str.Format{%0}}};
  283. ///=== {
  284. // #@ str.Left <字符串> <截取宽度>
  285. // 按照屏幕宽度标准,截取字符串左边的若干字符。
  286. // 本函数也可以简写为 left,两个名称用途和用法完全相同。
  287. //
  288. // #@ str.Right <字符串> <截取宽度>
  289. // 按照屏幕宽度标准,截取字符串右边的若干字符。
  290. // 本函数也可以简写为 right,两个名称用途和用法完全相同。
  291. //
  292. // #@ str.Sub <字符串> <开始宽度> <截取宽度>
  293. // 按照屏幕宽度标准,从字符串开始的某个宽度,截取连续的若干字符。
  294. // 本函数也可以简写为 substr,两个名称用途和用法完全相同。
  295. // };
  296. #func {str.Left} {#local width {%2}; #if {$width<=0} {#return {}} {#format result {%.${width}s} {%1}}};
  297. #func {left} {#return {@str.Left{%0}}};
  298. #func {str.Right} {#local width {%2}; #if {$width<=0} {#return {}} {#format result {%.-${width}s} {%1}}};
  299. #func {right} {#return {@str.Right{%0}}};
  300. #func {str.Sub} {#return {@str.Left{{@str.Right{{%1}; @math.Eval{@str.Width{%1} - %2}}}; %3}}};
  301. #func {substr} {#return {@str.Sub{%0}}};
  302. ///=== {
  303. // #@ str.AlignLeft <字符串> <宽度>
  304. // 按照屏幕宽度标准,在字符串后面补空白,延长至指定宽度并使之居左对齐。
  305. //
  306. // #@ str.AlignRight <字符串> <宽度>
  307. // 按照屏幕宽度标准,在字符串前面补空白,延长至指定宽度并使之居右对齐。
  308. // };
  309. #func {str.AlignLeft} {#format result {%-%2s} {%1}};
  310. #func {str.AlignRight} {#format result {%+%2s} {%1}};
  311. ///=== {
  312. // #@ str.AlignCenter <字符串> <宽度>
  313. // 按照屏幕宽度标准,在字符串前后补空白,延长至指定宽度并使之居中对齐。
  314. // };
  315. #func {str.AlignCenter} {
  316. #local str {%1};
  317. #local max {@default{%2;80}};
  318. #local width {@str.Width{$str}};
  319. #local left {};
  320. #local right {};
  321. #math left {($max - $width) / 2 + $width};
  322. #math right {$max - $left};
  323. #format result {%+${left}s%${right}s} {%1} {};
  324. #return {$result};
  325. };
  326. ///=== {
  327. // #@ str.Split <字符串> <分隔符>
  328. // 将字符串按照分隔符拆分成多个子串,并作为字符串列表返回。
  329. // 这里的分隔符可以通过正则表达式来指定。
  330. // 本函数也可以简写为 split,两个名称用途和用法完全相同。
  331. // };
  332. #func {str.Split} {
  333. #local str {%1};
  334. #local sep {%2};
  335. #replace str {$sep} {;};
  336. #return {$str};
  337. };
  338. #func {split} {#return {@str.Split{%0}}};
  339. ///=== {
  340. // #@ str.Chars <字符串>
  341. // 把字符串拆成一系列单个的字符,结果是由组成它的每个字符构成的字符串列表。
  342. // 本函数也可以简写为 chars,两个名称用途和用法完全相同。
  343. // };
  344. #func {str.Chars} {
  345. #local str {%0};
  346. #list str {tokenize} {$str};
  347. #return {@slist.FromList{$str}};
  348. };
  349. #func {chars} {#return {@str.Chars{%0}}};
  350. ///=== {
  351. // #@ util.ColorBar <字符串> <颜色1> <权重1> <颜色2> <权重2> [...]
  352. // 将字符串按照颜色及其对应的权重占比,渲染成彩色字符串。注意颜色参数须按顺序排列。
  353. // };
  354. #func {util.ColorBar} {
  355. #local str {%1};
  356. #local args {};
  357. #list args create {%0};
  358. #list args delete {1} {1};
  359. #local parts {};
  360. #list parts create {};
  361. #local count {0};
  362. #local sum {0};
  363. #while { $count < &args[] } {
  364. #local color {$args[@math.Eval{$count + 1}]};
  365. #local weight {@defaultNum{$args[@math.Eval{$count + 2}];0}};
  366. #list parts {add} {{
  367. {color}{$color}
  368. {weight}{$weight}
  369. }};
  370. #math count {$count + 2};
  371. #math sum {$sum + $weight};
  372. };
  373. #local elem {};
  374. #local len {@str.Len{$str}};
  375. #local leftLen {0};
  376. #local leftWeight {0};
  377. #local colorStr {};
  378. #foreach {$parts[%*]} {elem} {
  379. #local elemLen {@math.Eval{($elem[weight] + $leftWeight) * $len / $sum - $leftLen}};
  380. #local text {@str.Sub{{$str};$leftLen;$elemLen}};
  381. #cat colorStr {$elem[color]$text<299>};
  382. #math leftLen {$leftLen + $elemLen};
  383. #math leftWeight {$leftWeight + $elem[weight]};
  384. };
  385. #return {$colorStr};
  386. };