普贤教程 › 生态包
数据格式与编解码
29 个「输入输出」包:CSV、Excel、PDF、Markdown、HTML、YAML/TOML/INI/properties、JSONPath、multipart、SQL 解析、msgpack/protobuf/BSON、zlib/lz4、tar。共同点是纯函数、无 IO(读写文件由你调 read_file/write_file)。
CSV
px
import csv var r = csv_parse("name,age\n张三,28\n李四,31\n") print(r.ok()) # [[name, age], [张三, 28], [李四, 31]] print(csv_parse("a,\"b,c\",d").ok()) # [[a, b,c, d]] ← 引号内的逗号不切 print(csv_parse_header("a,b\n1,2").ok()) # [{a: 1, b: 2}] ← 首行作 header,直接得 dict 列表 print(csv_serialize([["a", "b,c"], ["1", "2"]]).ok()) # a,"b,c" # 1,2 ← 需要时自动加引号
0.2.0 起有 csv_parse_opts(s, opts):自定义分隔符、引号符、注释符、跳空行。国内导出常遇到 ; 分隔与 BOM,BOM 要先自己 trim 掉。
Excel(xlsx)
px
import xlsx var doc = xsx_new() doc = xsx_add(doc, "Sheet1", [["姓名", "销量"], ["张三", 100], ["李四", 80]]) print(xsx_write(doc, "/tmp/report.xlsx")) # Ok(0) print(xsx_read("/tmp/report.xlsx").ok()) # [[姓名, 销量], [张三, 100], [李四, 80]]
xsx_add 返回的是新 doc,不改原对象写成 xsx_add(doc, …) 而不接返回值,写出来的 xlsx 只有 5 个骨架文件、没有 worksheet——Excel 打开是空表,且不报错。必须 doc = xsx_add(doc, …)。pdf 的 pd_add_page / pd_add_text_fs 同理。写是「内存构建 → 一次落盘」,读是「整份进内存」。几千行没问题,十万行走不了——大表请走 CSV 或数据库导出。
px
import pdf var doc = pd_new() doc = pd_add_page(doc, 595, 842) # A4 doc = pd_add_text_fs(doc, 0, 72, 760, "月度报告", 20) pd_save(doc, "/tmp/report.pdf")
实测落盘 593 字节,文件头 %PDF-1.4。支持文字、线、矩形、元数据;不做字体嵌入(中文需确认字体可用)、图片、表格自动排版。要复杂排版,用 HTML → 打印到 PDF。
Markdown 与 HTML
px
import markdown print(md_render("# 标题\n\n正文 **加粗** 与 `代码`。\n\n- a\n- b\n").ok())
text
标题 正文 加粗 与 代码。 - a - b
px
print(md_extract_links("见 [文档](https://px.dzty.club)")) # [https://px.dzty.club] print(md_render_html("# 标题")) # 0.2.0 起有 HTML 输出 print(md_word_count("# 标题 正文")) # 词数
两套 Markdown 别混用
markdown 出纯文本(终端/日志用);richmd 出ANSI 彩色(终端展示用)。要 HTML 用 md_render_html。HTML 解析
px
import html var root = html_parse("<p>你好 <b>世界</b></p>") print(html_text(root)) # 你好 世界 print(html_query(root, "b")[0]["tag"]) # b print(html_escape("<a>"))
容错解析器:标签不闭合也能出树。抓网页正文用它,别用正则。
配置格式四兄弟
px
import toml print(toml_parse("a = 1\n[b]\nc = \"x\"\n").ok()) # {a: 1, b: {c: x}} import ini print(ini_parse("[db]\nhost = 127.0.0.1\n").ok()) # {: {}, db: {host: 127.0.0.1}} # 注意:无节区键会落在空字符串键下 import dotenv print(dotenv_parse("A=1\nB=\"x y\"\n")) # {A: 1, B: x y} ← 直接返回 dict,不是 Ok print(dotenv_get(dotenv_parse("A=1"), "A")) # 1 print(dotenv_validate(d, ["A", "B"])) # 必填项检查
返回形态不统一
toml_parse / ini_parse / yaml_parse 返回 Ok(dict);dotenv_parse 直接返回 dict。照抄别人的代码时最容易在这里报「dict 没有 ok 方法」。yaml 支持基础映射/序列/标量;properties 是 Java 风格;config 做的是「多层配置合并 + 点路径取值 + 类型转换」——多环境配置(默认值 ← 文件 ← 环境变量)用它最省事。
JSONPath
px
import jsonpath var d = {"a": {"b": [10, 20, 30]}} print(jp_get(d, "a.b[1]")) # Ok(20) print(jp_get(d, "a.b.1")) # Err(jsonpath: 无键: 1) ← 点号数字不行 print(jp_keys(d, "a")) # [b]
数组只能用
[n]点号后面跟数字会被当成对象键名去找,返回 Err(无键)。这是最容易写错的一处。二进制与压缩
px
import zlib var zc = zl_compress("hello hello hello").ok() # Ok(bytes) print(bytes_len(zc)) # 16 print(bytes_to_str(zl_uncompress(zc).ok())) # hello hello hello import lz4 var lc = lz_compress(bytes("abcabcabc")).ok() # 入参必须是 bytes print(bytes_to_str(lz_decompress(lc).ok())) # abcabcabc import msgpack var mp = mp_pack({"a": [1, 2], "b": "中文"}).ok() print(bytes_len(mp)) # 15 print(mp_unpack(mp).ok()) # {a: [1, 2], b: 中文} import bytes_pack print(bp_pack(">IH", [1, 65535]).ok()) # <bytes 6>
bytes 与 str 是两面
len() / s[i] 在 str 上是「字符」,二进制必须用 bytes_len / bytes_get / bytes_slice。print(bytes对象) 会显示 <bytes 17>,要看内容用 bytes_to_str 或十六进制打印。压缩选型
| 包 | 典型压缩比 | 特点 |
|---|---|---|
zlib | 高 | 通用,HTTP 同款,解压快 |
lz4 | 低 | 极快,适合日志/中间数据 |
tar / targz | — | 打包(tar_pack(files))与打包+压缩 |
序列化选型
| 包 | 格式 | 何时用 |
|---|---|---|
jsonx / 内建 | JSON | 与人/HTTP 交互(json_parse_opt) |
msgpack | MessagePack | 二进制、比 JSON 小且快 |
protobuf | Protobuf wire | 跨语言、有 .proto 契约 |
bson | BSON | 与 MongoDB 互操作 |
bytes_pack | struct 风格 | 定长二进制头(协议帧) |
SQL 解析
px
import sqlparse print(sq_parse("SELECT id, name AS n FROM users WHERE age > 18 ORDER BY id DESC LIMIT 5").ok()) # {columns: [{alias: , expr: {name: id, type: ident}}, …], distinct: false, # from: {alias: , table: users}, limit: 5, order_by: […], type: select, where: {…}}
SELECT 子集(含 WHERE 布尔表达式、ORDER BY、LIMIT)。用途是审计/改写 SQL——判断一条 SQL 有没有全表扫、有没有带 LIMIT;不要拿它当数据库驱动。
这一组包的通用纪律
- 解析外部输入一律用
_opt版本(json_parse_opt等):抛错的版本没有 try/catch 兜底,Web 里直接 500。 - 先判
is_err()再取ok(),ok()在 Err 上返回 null,静默传下去最难查。 - 大文件别进内存:xlsx / pdf / yaml 都是全量进内存的实现。