Python和PCRE正则表达式对相同输入输出不同

0 投票
2 回答
68 浏览
提问于 2025-04-14 16:45

我正在尝试在zig中实现minbpe库,并且使用了一个封装了PCRE库的方式。

在Python中的匹配模式是r"""'(?:[sdmt]|ll|ve|re)| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+"""

当我用这个模式去处理像abcdeparallel १२४这样的UTF-8编码文本时,得到的输出是:

>>> import regex as re
>>> p = re.compile(r"""'(?:[sdmt]|ll|ve|re)| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+""")
>>> p
regex.Regex("'(?:[sdmt]|ll|ve|re)| ?\\p{L}+| ?\\p{N}+| ?[^\\s\\p{L}\\p{N}]+|\\s+(?!\\S)|\\s+", flags=regex.V0)
>>> p.findall("abcdeparallel १२४")
['abcdeparallel', ' १२४']

看起来在PCRE的正则表达式中,这个模式差不多是一样的,只需要在最后加上一个/g标志来进行UTF-8匹配。

但是,当我尝试在macOS上通过pcre2test工具使用这个模式时,输出却完全不同。

$ pcre2test -8
PCRE2 version 10.42 2022-12-11
  re> /'(?:[sdmt]|ll|ve|re)| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+/g
data> abcdeparallel १२४
 0: abcdeparallel
 0:  \xe0
 0: \xa5\xa7
 0: \xe0
 0: \xa5\xa8
 0: \xe0
 0: \xa5
 0: \xaa

似乎印地语数字(1, 2, 4)的代码点被解释得不一样,输出的结果匹配成了一组完全不同的字符。

>>> "\xe0\xa5\xa7\xe0\xa5\xa8"
'१२'

我是不是漏掉了什么标志,或者有什么需要传递的参数,才能让它的行为和Python的regex包/模块一样?当UTF-8的代码点被解码成字节时,难道这个库不知道怎么把它们重新组合成相同的代码点吗?

2 个回答

0

你只需要用UTF-8来解码这些字节,而不是把它们当作字符串来处理。

>>> "\xe0\xa5\xa7\xe0\xa5\xa8"
'१२'
>>> b"\xe0\xa5\xa7\xe0\xa5\xa8".decode('utf-8')
'१२'

如果你想要的结果更像Python的那种,可以使用 utf8_input 选项(配合32位的pcre2test)或者在模式的开头加上 (*UTF)

$ pcre2test -32
PCRE2 version 10.42 2022-12-11
  re> /'(?:[sdmt]|ll|ve|re)| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+/g,utf8_input
data> abcdeparallel १२४
 0: abcdeparallel
 0:  \x{967}\x{968}\x{96a}

在Python中,你可以这样显示这些代码点:

>>> u"\u0967\u0968\u096a"
'१२४'
1

其实,印地语的代码点是匹配上的,但在屏幕上显示出来的是UTF-8的十六进制代码:

>>> "१२४".encode("utf-8")
b'\xe0\xa5\xa7\xe0\xa5\xa8\xe0\xa5\xaa'

根据pcre2test的说明

当pcre2test输出编译后的模式文本时,32到126以外的字节总是被视为不可打印字符,因此会以十六进制转义的形式显示。

当pcre2test输出的是匹配到的主题字符串的一部分时,它的表现也是一样,除非为模式设置了不同的区域设置(使用区域设置修饰符)。在这种情况下,会使用isprint()函数来区分可打印和不可打印字符。

说明中没有提到可以使用哪些区域设置。示例(fr_FR)暗示使用两位字母的国家代码和两位字母的语言代码,但我不太清楚印地语是否被支持。

使用`(*UTF)`标志时,你会得到两个匹配,印地语数字会以unicode的十六进制形式显示:

re> /(*UTF)(?:[sdmt]|ll|ve|re)| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+/g
data> abcdeparallel १२४
 0: abcdeparallel
 0:  \x{967}\x{968}\x{96a}

撰写回答