\p{P} 标点七子类:脚注符号到底归哪一类
码点的档案 的 gc 表里,\p{P}(Punctuation 标点)只占一行——但它切成 7 个子类 Pc Pd Ps Pe Pi Pf Po。传统脚注符号 * † ‡ § ¶ ‖ 全落在成员最多的 catch-all 子类
Po 里;而看着成对的开括号 / 闭括号(Ps / Pe)、前引号 / 后引号(Pi / Pf)数量却配不齐。这页把这 7 个子类全用原生 /\p{…}/u 实测着看清楚:每个码点恰好属于一个 gc 子类,首字母 P 就是它的大类。
注 · gc 子类是「身份」,不是「住址」。一个码点的 General_Category 在 UCD 里是单值的——* 的 gc 永远是 Po,无论它出现在脚注、乘法还是脚标里。这和 Block(住址)不同:住址只说它住哪段码位。本页所有「是不是某子类」都用 /^\p{…}$/u 当场测,不是查表。
1 · 六个脚注符号全是 Po
排版里第一个脚注用 *、第二个 †、第三个 ‡……传统排版有固定次序 * † ‡ § ‖ ¶,但这是排版约定的次序,Unicode 的码位顺序根本不照它走:§ ¶ 早早躺在 U+00A7 / U+00B6、‖ 在 U+2016、†
‡ 才到 U+2020 / U+2021,被彻底打散。它们在 gc 里没有专属子类,一律归入「其他标点 Po」。
注 · 对照着看:‖(U+2016)是 Po,而长得像它的管道符 |(U+007C)却是 Sm(数学符号,根本不在 \p{P} 里);§ ¶ 在 Unicode 6.1(2012)之前曾是
So(其他符号),之后才并入 Po——老数据可能仍标成 Symbol。
注 · 不止 gc——任何可查属性都不编码「能当脚注」。把最沾边的二元属性挨个试过去也全落空:这六个符号 \p{Terminal_Punctuation} / \p{Sentence_Terminal} / \p{Dash} / \p{Quotation_Mark} 一律 No。唯一「全 Yes」的是
\p{Pattern_Syntax}——但它是干扰项:它的含义是「保留给编程语法、不可作标识符」,把 ! ? % & 和上百个符号一并圈入,同样挑不出脚注。
2 · 按字符 Name 筛选候选
既然六个脚注符号在 gc 里全是 Po、和 ! ? % & 同属 641 个成员的 catch-all 子类,那「哪些字符能当脚注 / 引用记号」这件事 category 根本不编码——它是排版约定(editorial convention,Chicago / Oxford 等 style manual
沿袭的活字传统),不是字符属性。Unicode 唯一沾边的可机读线索是字符的 Name(及 NamesList.txt 注解),比如 U+203B 干脆就叫 REFERENCE MARK。所以只能反过来按名字筛选候选。
警示 · 两点结论。其一,用 gc 选不出脚注符号——Po 里九成以上跟脚注无关,「属于 Po」推不出「能当脚注」。其二,即便改用 Name 关键词,命中也横跨 Po / Sm / So / Mn / Lo / Cf——「名字里有
ASTERISK」不等于「是标点」(∗ ASTERISK OPERATOR 是 Sm、❃ 是 So 装饰、◌͙ COMBINING ASTERISK BELOW 是 Mn),更不等于「能当脚注」。Name 是线索不是标签;footnote-ness 这种语义 Unicode 不编码——真要判定,得查 style manual,不是查码点属性。
注 · 连 NamesList 注解也只是线索,给不了判定。反例两头都有:U+070D SYRIAC HARKLEAN ASTERISCUS 的注解明写 marks the beginning of a phrase, word, or morpheme that has a marginal note——语义上就是旁注记号、名字也含 ASTERIS、gc 还是
Po,却没被收进上面的候选(它是 Syriac 专用);反过来 ⁂ ASTERISM(U+2042)在 NamesList 里一条注解都没有,却作为正统排版记号被收了。可见「是不是脚注符号」最终由 style manual 的排版约定裁定——UnicodeData / NamesList 只能帮着缩小候选,最后一步还得人工按约定取舍。
3 · 七个子类的实测基数
每个码点恰好属于其中一个。Po 是 catch-all 子类,独占四分之三。
| 子类 | 名称 | 码点数 | 典型成员 |
|---|---|---|---|
Po |
Other 其他标点 | 641 | . , ! ? ; : … · * † ‡ § ¶ ‖ 、 。 |
Ps |
Open 开标点 | 79 | ( [ { 「 『 ( |
Pe |
Close 闭标点 | 77 | ) ] } 」 』 ) |
Pd |
Dash 横线标点 | 27 | - – — ~ |
Pi |
Initial 前引号 | 12 | « ‹ “ ‘ ‛ ‟ |
Pf |
Final 后引号 | 10 | » › ” ’ |
Pc |
Connector 连接标点 | 10 | _ ‿ ⁀ _ |
4 · 探针:任意字符落在哪个子类
_(下划线竟是标点 Pc)、|(不是标点,是 Sm)、、(顿号 Po)。5 · 七子类着色器
把一段中英混排文本逐码点上色,属于 \p{P} 的按七子类着色、其余灰显,就能看清 gc 怎么在真实标点上切分。
6 · 开闭与引号为什么配不齐
直觉上「有开就有闭」,可实测数量对不上:Ps 79 不等于 Pe 77、Pi 12 不等于 Pf 10,两组各差 2。根源是同一件事——引号天生塞不进「开 / 闭」二分法。gc 标的是字符自身的形状与角色,不是配对关系,于是配对就破了。
警示 · Ps 比 Pe 多 2:德式低位引号 ‚(U+201A)„(U+201E)被归成开标点 Ps(德语 „…“ 在基线起头),但它们的「闭合形」是抬高的 ‘ “,而那俩被归进了
Pi 而非 Pe——于是这两个低引号在 Ps 有身位、Pe 找不到对应。Pi 比 Pf 多 2:反 9 字形引号 ‛(U+201B)‟(U+201F)是「翻转当开引号用」的变体,没有专属闭合形(要闭合直接用普通的
’ ”),于是 Pi 这边凭空多两个。
建议 · 那「成对」到底记在哪?真正需要左右镜像的是括号,Unicode 用另一套正交属性专门管:Bidi_Paired_Bracket / Bidi_Paired_Bracket_Type(open / close / none)和 Bidi_Mirrored。引号因为跨语言方向不固定,根本没被纳入 bracket
配对。所以「成对」靠这套属性表达,而不是指望 Ps 数量等于 Pe。可惜 JS 正则的 \p{…} 不支持 Bidi 系列属性(和 Block 一样查不到),本页只能用 gc 把现象呈现出来。
建议 · 本页是 码点的档案 里 gc「七大类 30 小类」表中 \p{P} 那一行的展开,和 \p{Math} 全量速查 是「按身份深挖一类」的两个样本——一个是 gc 子类、一个是二元属性。
7 · 参考文献
- Unicode. UAX #44 §General_Category Values. gc 全部大类与小类的官方定义表:
Pc/Pd/Ps/Pe/Pi/Pf/Po各自收哪些字符、判定依据,以及它是单值属性这件事的出处。unicode.org -
Unicode. UAX #9: Unicode Bidirectional Algorithm. §3.1.3 的 BD14–BD16 定义括号「成对」怎么识别——为什么配对信息落在
Bidi_Paired_Bracket上、不在 gc 的 Ps/Pe 数量上。属性本身定义在 UCD 的BidiBrackets.txt,Bidi_Mirrored定义在核心规范 §4.7。unicode.org - Codepoints. 逐码点数据库. 查任意码点的 gc、
Bidi_Paired_Bracket、所属 block 等全部属性——核对本页四个落单引号的归类与配对关系最直接。codepoints.net