未记录的 Java 正则表达式字符类:\pC
Posted
技术标签:
【中文标题】未记录的 Java 正则表达式字符类:\\pC【英文标题】:Undocumented Java regex character class: \pC未记录的 Java 正则表达式字符类:\pC 【发布时间】:2017-10-17 11:16:31 【问题描述】:我在一个 Java 项目中发现了一个有趣的正则表达式:"[\\pC&&\\S]"
我知道&&
的意思是“设置交集”,\S
是“非空白”,但是\pC
是什么,可以用吗? p>
java.util.regex.Pattern documentation 没有提及。列表中唯一类似的类是\pCntrl
,但它们的行为不同:它们都匹配控制字符,但\pC
匹配U+FFFF 以上的Unicode 字符两次,例如PILE OF POO
:
public class StrangePattern
public static void main(String[] argv)
// As far as I can tell, this is the simplest way to create a String
// with code points above U+FFFF.
String poo = new String(Character.toChars(0x1F4A9));
System.out.println(poo); // prints `????`
System.out.println(poo.replaceAll("\\pC", "?")); // prints `??`
System.out.println(poo.replaceAll("\\pCntrl", "?")); // prints `????`
我在任何地方发现的唯一提及是here:
\pC 或 \pOther:不可见的控制字符和未使用的代码点。
但是,\pOther
在 Java 中似乎不存在,匹配的代码点也没有使用。
我的 Java 版本信息:
$ java -version
java version "1.8.0_92"
Java(TM) SE Runtime Environment (build 1.8.0_92-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.92-b14, mixed mode)
额外问题:原始模式"[\\pC&&\\S]"
的可能意图是什么?它发生在一个在通过电子邮件发送之前验证字符串的方法中:如果该模式匹配,则会引发消息“无效字符串”的异常。
【问题讨论】:
释义 unicode.org/reports/tr44,pC
可以是控制代码、格式控制字符、代理代码点、私人使用字符、保留的未分配代码点或非字符。这是因为“代理代码点”的事情(以及 Java 对 UTF-16 的略微狡猾的实现),一堆便便匹配其中的两个。
您的代码有点误导。正则表达式实际上只匹配低代理项(第二个char
),但是您的控制台可能将未配对的高代理项显示为?
。使用不同的字符作为替换来查看。
【参考方案1】:
在 Unicode Support 下的 Pattern 文档中,我们发现以下内容:
此类符合Unicode Technical Standard #18: Unicode Regular Expression 的级别 1,以及 RL2.1 规范等效项。
...
Categories 可以用可选前缀来指定:Both \pL 和 \pIsL 表示 Unicode 字母的类别。与脚本相同 和块,类别也可以使用关键字指定 general_category(或其缩写形式 gc)如 general_category=Lu 或 gc=Lu。
支持的类别是 Unicode 标准中的类别 由 Character 类指定的版本。类别名称是那些 标准中定义的规范性和信息性。
从Unicode Technical Standard #18,我们发现C
被定义为匹配任何Other General_Category 值,并且对此的支持是1 级一致性要求的一部分。 Java 实现了\pC
,因为它声称符合 UTS #18 的第 1 级。
它可能应该支持\pOther
,但显然不支持。
更糟糕的是,它违反了RL1.7,这是 1 级一致性所必需的,它要求匹配发生在代码点而不是代码单元:
为满足此要求,实现应处理所有 Unicode 代码点范围,包括从 U+FFFF 到 U+10FFFF 的值。 特别是在使用 UTF-16 的情况下,由前导代理后跟尾随代理组成的序列在匹配时应作为单个代码点处理。
您的测试字符串中不应有与 \pC
匹配的内容,因为您的测试字符串应作为单个表情符号代码点与 General_Category=So(其他符号)而不是两个代理项进行匹配。
【讨论】:
@Hulk:该标志用于一组不同的字符类,特别是在“预定义字符类”和“POSIX 字符类(仅限 US-ASCII)”下列出的那些。\pC
不是其中之一。
相关错误报告:JDK-8179668, JDK-8029966【参考方案2】:
根据https://regex101.com/,\pC 匹配
不可见的控制字符和未使用的代码点
(必须将 \ 转义,因为 java 字符串,所以字符串 \\pC 是正则表达式 \pC)
我猜这是一个“黑客字符串检查”,因为 \pC 可能永远不会出现在有效(字符填充)字符串中,但作者应该留下评论作为他们检查的内容和他们的内容想要检查的东西通常是 2 种不同的东西。
【讨论】:
【参考方案3】:除了有效的两字母 Unicode 类别代码或以 Unicode 类别代码开头的单个字母之外的任何内容都是非法的,因为 Java 仅支持 Unicode 类别的单字母和两字母缩写。这就是\pOther
在这里不起作用的原因。
\pC
与U+FFFF
以上的 Unicode 字符匹配 两次,例如 PILE 便便。
没错。 Java 在内部对 Unicode 字符使用 UTF-16 编码,? 被编码为两个 16 位代码单元 (0xD83D 0xDCA9
),称为代理对 (high surrogates),因为 \pC
分别匹配每一半
\pCs
或\pSurrogate
:UTF-16 中代理对的一半 编码。
您在结果集中看到两个匹配项。
原始模式
[\\pC&&\\S]
的可能意图是什么?
我看不出有什么正当理由,但似乎开发人员担心Other
类别中的字符(例如避免电子邮件主题中的垃圾邮件goomojies
),所以只是试图阻止它们。
【讨论】:
您在引号中突出显示的前两个语句的来源是什么?会很有趣,因为它似乎与当前投票最多的答案 ***.com/a/44034552/2513200 相矛盾【参考方案4】:至于奖励问题:表达式 [\\pC&&\\S] 查找控制字符,不包括 Java 中的制表符或换行符之类的空白字符。这些字符在普通邮件中没有任何价值,因此最好将它们过滤掉(或者,在这种情况下,将电子邮件内容声明为错误)。请注意,双反斜杠 (\\) 仅用于转义 Java 处理的表达式。正确的正则表达式是:[\pC&&\S]
【讨论】:
以上是关于未记录的 Java 正则表达式字符类:\pC的主要内容,如果未能解决你的问题,请参考以下文章
Java - 在正则表达式中转义元字符 [ 和 ] [重复]