有没有一个正则表达式可以匹配单个字符簇?

10

Graphemes是文本中用户感知的字符,可能由多个Unicode代码点组成。

来自Unicode®标准附录#29

重要的是要认识到,用户认为是“字符”的语言书写系统的基本单位可能不仅仅是单个Unicode代码点。相反,该基本单位可以由多个Unicode代码点组成。为避免与计算机使用的术语字符的歧义,这被称为用户感知字符。例如,“G” +重音符号是一个用户感知的字符:用户认为它是一个单一的字符,但实际上由两个Unicode代码点表示。这些用户感知字符由称为字形簇的东西近似,可以通过编程确定。

是否有我可以使用的正则表达式(在javascript中),它将匹配单个字形簇?例如

"한bar".match(/*?*/)[0] === "한"
"நிbaz".match(/*?*/)[0] === "நி"
"aa".match(/*?*/)[0] === "a"
"\r\n".match(/*?*/)[0] === "\r\n"
"‍♂️foo".match(/*?*/)[0] === "‍♂️"

1
您可以使用Unicode转义序列来匹配单个UTF-16或双UTF-16代理对,但我认为匹配完整的图形簇会非常复杂。肯定没有内置的方法可以做到这一点;JavaScript正则表达式对于像这样的通用Unicode模式来说是非常不足的。 - Pointy
请查看标记的问题,如果它没有回答你的问题,请相应地进行编辑。 - revo
1
@revo 我不认为这个问题是被标记的那个问题的重复。首先,它非常不清楚它在问什么(它开始询问如何用表情符号替换字符串中的表情符号等效项,并转而询问如何用它们的Unicode转义序列等效项替换表情符号)。其次,该问题明确提到了表情符号,而且问题和任何答案都没有涉及除表情符号之外的其他类型的字形簇(大多数表情符号由单个代码点表示)。我该如何申诉这个决定? - brainkim
1
Perl风格的正则表达式使用\X来匹配一个字符簇,但不幸的是,这似乎没有被JavaScript风格的正则表达式所支持...如果它们支持使用给定的Unicode属性来匹配代码点,那么您可能可以将Unicode规范中的EGC语法转换为正则表达式。 - Shawn
\X 匹配所有字符(无论字节数,例如 a),以及作为一个匹配的字形簇。它的工作方式与 ES6 支持并且可以转换为 ES5 的 \PM\pM* 几乎相同(您可以使用 此工具 进行转换)。但是这两者之间存在差异,即 \X 对于韩文音节(您在示例中使用了它们)有一些规则,即不会在其上断开。因此,您必须单独匹配它们。有关更多有关 Hangul 的见解,请参见 此处 - revo
显示剩余3条评论
1个回答

11

全面易用的集成支持:无。各种匹配任务的近似值:是。来自正则表达式教程

在 Perl、PCRE、PHP、Boost、Ruby 2.0、Java 9 和 Just Great Software 应用程序中,匹配单个字符(无论是作为一个代码点编码还是使用组合标记作为多个代码点)很容易:只需使用 \X。可以将 \X 视为点的 Unicode 版本。不过有一个区别:\X 总是匹配换行符,而点除非启用了点匹配换行符匹配模式,否则不会匹配换行符。

在 .NET、Java 8 及之前版本和 Ruby 1.9 中,您可以使用 \P{M}\p{M}+ 或 (?>\P{M}\p{M}) 作为相当接近的替代品。要匹配任意数量的字符,请使用 (?>\P{M}\p{M}*)+ 作为 \X+ 的替代品。

\X 是最接近的,但在任何 ES6 版本中都不存在。 \P{M}\p{M}+ 近似于 \X,但不存在这种形式:如果您通过原生或转译方式使用 ES6,则可以使用 /(\P{Mark})(\p{Mark}+)/gu

但即使如此,这还不够<== 阅读该链接获取所有细节。

提出了一种分段文本的建议,但尚未被采纳。如果您专注于 Chrome,则可以使用其非标准的Intl.v8BreakIterator来分解簇并手动匹配。


喜欢一些没有评论的随机下投票。 ;) - bishop
1
刚刚测试了一下,在 Node 14 中仍然不支持 \X - Old Geezer
1
\P{Mark} doesn't work for skin-toned emojis, which are pretty common these days. ''.replace(/(\P{Mark})(\P{Mark}+)/gu, '$1;$2') produces broken ";" - Oleg Mihailik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接