px普贤教程PuXian
普贤教程 › 生态包

文本加工进阶

七个「把文本改成想要的样子」的包:换行与截断、命名风格转换、URL slug、简繁、英文单复数、自然排序、终端 Markdown 渲染。它们几乎全在写 CLI 与后台报表时用到,也都各带一处「名字骗人」的地方。

textwrap:换行与截断

px
import textwrap

var s = "普贤是一门静态编译的语言,产物是零依赖静态二进制。写 Web 接口与命令行工具都很省事。"
print(tw_fill(s, 24))
print(tw_shorten(s, 12))
print(tw_center("hi", 10, "-"))
text
普贤是一门静态编译的语言,产物是零依赖静态二进制。写
Web 接口与命令行工具都很省事。
普贤是一门静态编译...
----hi----
只在空白处断行 —— 中文整句不会换行
tw_fill 先按空白切词,再往行里塞,宽度只是上限检查、不是强制断点。一段没有空格的中文只会得到一行,长度照样超过 width: 上面第一段 26 个字、width=24,照样一行输出。
反过来,带空格的中文能正常折行:
px
var s = "普贤 是 一门 静态 编译 的 语言 产物 是 零依赖 静态 二进制"
print(tw_fill(s, 12))
text
普贤 是 一门 静态
编译 的 语言 产物 是
零依赖 静态 二进制

另外它按 len() 数字符(一个汉字算 1),而终端里汉字占两格,所以中文行在屏幕上看着比 width 更宽。tw_word_count 同样是按空白切词:英文段落里的中文只数出 3 个词,中文分词请用 seg 包。

还有:tw_dedent(去公共缩进)、tw_indent、tw_ljust/rjust、tw_split_paragraphs、tw_line_count、tw_expandtabs、tw_fill_indent(首行/续行不同缩进)。

strcase:命名风格

px
import strcase

print(str_snake("HelloWorld"), str_kebab("HelloWorld"), str_camel("hello_world"))
print(str_pascal("hello world"), str_screaming_snake("helloWorld"))
print(str_title_words("hello world"), str_dot("a/b/c"), str_path("A/B"))
print(str_swap_case("Hello"), str_capitalize("hello"))
text
hello_world hello-world helloWorld
HelloWorld HELLO_WORLD
Hello World a.b.c a/b
hELLO Hello

str_path 处理文件路径(保斜杠),str_dot 处理包名(点分隔),两者不能互换。str_words 能把任意风格拆成词列表,是自定义转换的起点。

slug:URL 别名

px
import slug

print(sl_slug("普贤 PuXian 教程 —— 第 1 章"))   # 普贤-puxian-教程-第-1-章
print(sl_slug_ascii("普贤 PuXian 教程"))        # puxian
print(sl_slug_max("a very long title with many words indeed", 12))  # a-very-long
print(sl_is_valid("hello-world"), sl_unslug("hello-world"))
text
普贤-puxian-教程-第-1-章
puxian
a-very-long
true Hello World
sl_slug 会保留汉字——它判断 CJK 区段并原样留下,中文 slug 在 URL 里是合法的(浏览器会百分号编码)。要纯 ASCII 别名用 sl_slug_ascii。另备 sl_slug_unique(s, existing) 处理重名、sl_slug_id(s, suffix) 加唯一后缀。

hant:简繁,别当成生产可用

px
import hant

print(hant_s2t("汉语编程很简单,服务器网络数据库").ok())
print(hant_count_traditional("汉语编程"))
text
汉语编程很简單,服務器网络數據库
16
映射表只有 19 组字
hant_s2t 只转换表里 19 个字(单→單、务→務、据→據…),汉→漢、语→語、编→編 都不在表里,所以输出是简繁混排。 hant_count_traditional 更不可靠:它把「汉语编程」(4 个纯简体字)数成 16,等于全量计数。要做真正的简繁转换,用外部词典,不要用这个包。

inflect:英文单复数与冠词

px
import inflect

print(if_plural("box"), if_plural("cat"), if_plural("city"))
print(if_singular("boxes"), if_singular("cities"))
print(if_ordinal(1), if_ordinal(2), if_ordinal(3), if_ordinal(21))
print(if_article("apple"), if_article("hour"), if_article("university"))
text
Ok(boxes) Ok(cats) Ok(cities)
Ok(box) Ok(city)
1st 2nd 3rd 21st
an an a

复数/单数返 Result(不规则表查不到时是 Err,可以自己兜底),序数与冠词返纯字符串。if_article 是按读音判的:hour 用 an、university 用 a。

natsort:自然排序

px
import natsort

var l = ["img10.png", "img2.png", "img1.png"]
print(str(na_sorted(l)))        # [img1.png, img2.png, img10.png]
print(str(l))                   # 原列表不变:[img10.png, img2.png, img1.png]
print(na_compare("a2", "a10"))  # -1
print(str(na_sorted(["v1.10", "v1.9", "v1.2"])))   # [v1.2, v1.9, v1.10]
text
[img1.png, img2.png, img10.png]
[img10.png, img2.png, img1.png]
-1
[v1.2, v1.9, v1.10]

na_sorted 返回新列表,不改原列表——这点和 Python 的 list.sort() 相反。na_key 能取出排序键,配合自己的比较逻辑用。

richmd:终端里的 Markdown

px
import richmd

var src = "# 标题\n\n这是 `代码` 与 **粗**。\n\n> 引用\n\n1. 一\n2. 二"
var r = mdr_render(src)
print(r.ok())               # 带 ANSI 转义
print(mdr_strip(r.ok()).ok())   # 去掉转义后的纯文本
text
标题
这是 代码 与 粗。
▌ 引用
1. 一
2. 二

上面第二行是 mdr_strip 的结果(实际输出带颜色码,这里只示意文本)。

mdr_strip 去的是 ANSI,不是 Markdown 标记
名字像「去掉格式」,实际做的是「去掉终端转义序列」。对原始 Markdown 调它,**粗** 会原样留着。
真正把 Markdown 转成纯文本/HTML 要用 mdr_render_plain / mdr_render_html,而 mdr_render_html 只处理块级(标题、段落、列表变段落),行内 **粗** / `code` 原样留在 HTML 里——当网页用会露馅。

实测 mdr_render_html("# T\n\n**b**") 得到 <h1>T</h1> + <p>**b**</p>。要完整 Markdown 渲染,用 markdown 包(见《数据格式与编解码》)。