文本与格式化实战
这 12 个包补的是「写业务逻辑时天天要用、但语言本体没给」的那一层:数组操作、类型判断、格式化、相似度、进制、语音编码、颜色。全部纯函数,双模式(解释轨 / 编译轨)输出一致。
一个脚本跑完 12 个包
import arrutil import compact import ctype import multisort import numconv import phonetic import simtext import sprintf import strpad import textfmt import vardump import colorutil def main(): print(json_stringify(arr_flip({"a": 1, "b": 2}))) print(json_stringify(arr_column([{"id": 1, "name": "甲"}, {"id": 2, "name": "乙"}], "name"))) print(json_stringify(arr_chunk_fixed([1, 2, 3, 4, 5], 2))) print(json_stringify(arr_unique_stable([3, 1, 3, 2, 1]))) print(json_stringify(cp_compact(["host", "port"], ["127.0.0.1", 8080]))) print(json_stringify(cp_only({"a": 1, "b": 2, "c": 3}, ["a", "c"]))) print(json_stringify(cp_pluck([{"id": 1}, {"id": 2}], "id"))) print(str(ct_digit("12345")) + " " + str(ct_digit("12a")) + " " + str(ct_alpha("abc")) + " " + str(ct_xdigit("ff00"))) var rows = [{"n": "乙", "age": 30}, {"n": "甲", "age": 25}, {"n": "丙", "age": 30}] print(json_stringify(ms_sort_by(rows, ["age"], ["desc"]))) print(nc_dec2hex(255) + " " + nc_dec2bin(10) + " " + nc_dec2roman(1987) + " " + str(nc_base_convert("ff", 16, 36))) print(ph_soundex("Robert") + " " + ph_soundex("Rupert") + " " + str(ph_match("Smith", "Smyth"))) print(str(st_similar("kitten", "sitting")) + "% " + str(st_lcs("ABCBDAB", "BDCABA")) + " " + st_lcs_str("kitten", "sitting")) print(sf_format("%05.2f|%+d|%x|%-6s|", [3.14159, 42, 255, "ab"])) print("[" + sp_pad("7", 3, "0", "left") + "] [" + sp_pad_both("mid", 9, "-") + "] [" + sp_truncate("一口气写完这篇文档", 8) + "]") print(tf_nl2br("line1\nline2")) print(tf_strtr_table("Hello {name}", {"{name}": "世界"})) print(vd_dump({"ok": true, "n": 3})) print(json_stringify(co_hex_to_rgb("#FF8800"))) print(co_lighten("#3366cc", 0.2) + " " + co_complement("#3366cc") + " " + str(co_contrast_ratio("#000000", "#ffffff")))
{"1":"a","2":"b"}
["甲","乙"]
[[1,2],[3,4],[5]]
[3,1,2]
{"host":"127.0.0.1","port":8080}
{"a":1,"c":3}
[1,2]
true false true true
[{"n":"乙","age":30},{"n":"丙","age":30},{"n":"甲","age":25}]
ff 1010 MCMLXXXVII 73
R163 R163 true
61.53846153846154% 4 ittn
03.14|+42|ff|ab |
[007] [---mid---] [一口气写完...]
line1<br>
line2
Hello 世界
array(2) {
["ok"]=>
bool(true)
["n"]=>
int(3)
}
[255,136,0]
#84A3E0 #CC9932 21.0arrutil:19 个数组函数
arr_flip / arr_column / arr_chunk_fixed / arr_unique_stable / arr_zip / arr_flat / arr_rotate / arr_partition / arr_sliding … 名字基本能对上 PHP 的 array_*。
arr_flip({"a":1}) 得到的是 {"1":"a"},不是 {1:"a"}。普贤的 dict 只接受字符串键,所以「翻转后按数字键取值」要写 d[str(1)]。arr_unique_stable 保序([3,1,3,2,1] → [3,1,2]),需要排序去重就再接 sort。compact:变量与字典互转
cp_compact(["host", "port"], ["127.0.0.1", 8080]) # {"host":"127.0.0.1","port":8080} cp_only({"a":1,"b":2,"c":3}, ["a", "c"]) # {"a":1,"c":3} cp_except({"a":1,"b":2}, ["b"]) # {"a":1} cp_pluck([{"id":1},{"id":2}], "id") # [1,2] cp_flip({"a":1}) # {"1":"a"}
这是从 PHP 迁过来最省事的一组:compact('host','port') 在普贤里没有对应语法(局部变量不可枚举),所以显式传名字列表。
ctype:整串判断 + 单字符判断
两套接口:ct_digit(s) 判断整串是否全数字,ct_is_digit(c) 判断单个字符。空串一律返回 false(PHP 的 ctype_digit("") 也是 false,口径一致)。
ct_alpha("中文") 是 false——这套函数按字节判断 ASCII。中文合法性请用 validator 包的字符类规则。multisort:多键联动排序
var rows = [{"n":"乙","age":30}, {"n":"甲","age":25}, {"n":"丙","age":30}] print(json_stringify(ms_sort_by(rows, ["age"], ["desc"]))) # 同值时保持原顺序(稳定排序)
两个入口:ms_sort(arrays, orders) 是 PHP array_multisort 的等价物(多个平行数组一起排);ms_sort_by(rows, keys, orders) 排 list[dict],日常用后者。
numconv:进制与罗马数字
nc_dec2hex(255) # ff nc_dec2bin(10) # 1010 nc_dec2roman(1987) # MCMLXXXVII nc_base_convert("ff", 16, 36) # 73 nc_dec2words(42) # forty-two
支持 2–64 进制。注意 nc_base_convert 的参数顺序是 (字符串, 源进制, 目标进制),与 PHP base_convert 的 (数字, 从, 到) 一致,但返回值是字符串。
phonetic:三种语音编码
ph_soundex("Robert") # R163 ph_soundex("Rupert") # R163 ← 同音同码 ph_match("Smith","Smyth") # true
Soundex / Metaphone / NYSIIS 三种都有,ph_distance 给编辑距离。只对拉丁字母有意义,中文拼音要先转写。
simtext:相似度与 LCS
st_similar("kitten", "sitting") # 61.53846153846154(百分比,float) st_lcs("ABCBDAB", "BDCABA") # 4 st_lcs_str("kitten", "sitting") # ittn st_jaro_winkler("martha", "marhta") # 0.961...
st_similar 沿用 PHP similar_text 的算法与口径,结果是浮点百分比。要整数自己 int() 截断。字符串很长时这个算法是递归 LCS,复杂度不低——比对几千字符以上的文本用 st_lcs(DP)更稳。sprintf:C 风格格式化
sf_format("%05d", [42]) # 00042 sf_format("%8.3f", [3.5]) # 3.500 sf_format("%08.3f", [3.5]) # 0003.500 sf_format("%+05d", [42]) # +0042 sf_format("%x|%-6s", [255, "ab"]) # ff|ab
%08.3f → 0003.500、%+05d → +0042);宽度不够时左补填充字符,%-6s 左对齐。strpad:填充与截断
sp_pad("7", 3, "0", "left") # 007 sp_pad_both("mid", 9, "-") # ---mid--- sp_truncate("一口气写完这篇文档", 8) # 一口气写完...(按字符算,不是字节) sp_repeat("ab", 3) # ababab sp_zeropad(42, 5) # 00042
按字符计宽,不是字节——中文不会被切坏。这是它比手写 substr 强的地方。
textfmt:PHP 文本函数合集
tf_nl2br("line1\nline2") # line1<br>\nline2 tf_strtr_table("Hello {name}", {"{name}":"世界"}) # Hello 世界 tf_qp_encode("中文 = 编码") # Quoted-Printable tf_wordwrap("一段很长的话…", 20, "\n", true) tf_addslashes("it's") # it\'s
最常用的是 tf_strtr_table:给一个映射字典批量替换,比链式 replace 快且不会互相覆盖。注意 tf_nl2br 输出的是 <br>,直接塞进 HTML 可以;要塞进属性或 JSON 先 hs_escape。
vardump:三种转储格式
vd_dump(带类型与长度)、vd_export(可当代码复用)、vd_print_r(人读)。调试接口时把 vd_export 的结果贴进日志,比 json_stringify 更容易看出类型。
json_stringify。colorutil:颜色换算
co_hex_to_rgb("#FF8800") # [255,136,0] co_lighten("#3366cc", 0.2) # #84A3E0 co_complement("#3366cc") # #CC9932 co_contrast_ratio("#000000", "#ffffff") # 21.0 co_is_dark("#101010") # true
对比度用的是 WCAG 口径(1–21)。做配色检查时 正文对比度 ≥ 4.5、大字号 ≥ 3.0,co_contrast_ratio 直接给判定值。它不做取色(从图片里取主色),那是 gfx / png 的活。