【问题标题】:Python regex matching Unicode properties匹配 Unicode 属性的 Python 正则表达式
【发布时间】:2010-12-22 10:02:42
【问题描述】:

Perl 和其他一些当前的正则表达式引擎在正则表达式中支持 Unicode 属性,例如类别。例如。在 Perl 中,您可以使用 \p{Ll} 匹配任意小写字母,或使用 p{Zs} 匹配任何空格分隔符。我在 Python 的 2.x 和 3.x 行中都没有看到对此的支持(很遗憾)。有人知道获得类似效果的好策略吗?欢迎使用本土解决方案。

【问题讨论】:

  • 实际上,Perl 支持 所有 Unicode 属性,而不仅仅是一般类别。示例包括\p{Block=Greek}, \p{Script=Armenian}, \p{General_Category=Uppercase_Letter}, \p{White_Space}, \p{Alphabetic}, \p{Math}, \p{Bidi_Class=Right_to_Left}, \p{Word_Break=A_Letter }, \p{Numeric_Value=10}, \p{Hangul_Syllable_Type=Leading_Jamo}, \p{Sentence_Break=SContinue}, 和大约 1,000 多个。只有 Perl 和 ICU 的正则表达式会费心去覆盖所有 Unicode 属性。其他人只涵盖了一小部分,通常甚至不足以进行最少的 Unicode 工作。

标签: python regex unicode ucd character-properties


【解决方案1】:

你说得对,Python 正则表达式解析器不支持 Unicode 属性类。

如果你想做一个很好的 hack,这通常会很有用,你可以创建一个预处理器来扫描字符串中的此类标记(\p{M} 或其他)并用相应的字符集替换它们,这样,例如,\p{M} 将变为 [\u0300–\u036F\u1DC0–\u1DFF\u20D0–\u20FF\uFE20–\uFE2F]\P{M} 将变为 [^\u0300–\u036F\u1DC0–\u1DFF\u20D0–\u20FF\uFE20–\uFE2F]

人们会感谢你的。 :)

【讨论】:

  • 是的,创建角色类闪过我的脑海。但是大约有 40 个类别,你最终会产生 80 个类,这还不包括 unicode 脚本、块、平面等等。可能值得一个小开源项目,但仍然是维护的噩梦。我刚刚发现 re.VERBOSE 不适用于字符类,所以这里没有 cmets 或空格来帮助可读性......
【解决方案2】:

您是否尝试过Ponyguruma,这是一个与Oniguruma 正则表达式引擎的Python 绑定?在该引擎中,您可以简单地说 \p{Armenian} 来匹配亚美尼亚字符。 \p{Ll}\p{Zs} 也可以。

【讨论】:

【解决方案3】:

请注意,虽然\p{Ll} 在 Python 正则表达式中没有等效项,但 \p{Zs} 应该被 '(?u)\s' 覆盖。 (?u),正如文档所说,“使 \w、\W、\b、\B、\d、\D、\s 和 \S 依赖于 Unicode 字符属性数据库。” \s 表示任何空格字符。

【讨论】:

  • 你是对的。问题是,'(?u)\s' 大于 '\p{Zs}',包括例如新队。所以如果你真的想只匹配空格分隔符,前者是过度生成的。
  • @ThomasH:要获得“除非换行符之外的空格”,您可以使用双重否定字符类:(?u)[^\S\n]
【解决方案4】:

您可以在每个字符上煞费苦心地使用 unicodedata:

import unicodedata

def strip_accents(x):
    return u''.join(c for c in unicodedata.normalize('NFD', x) if unicodedata.category(c) != 'Mn')

【讨论】:

  • 谢谢。虽然在正则表达式之外,但这对于某些情况可能是可行的替代方案。
  • 看来 Python unicodedata 模块目前不包含有关例如字符的脚本或 Unicode 块。另见stackoverflow.com/questions/48058402/…
【解决方案5】:

regex 模块(标准 re 模块的替代品)支持使用 \p{} 语法的 Unicode 代码点属性。

【讨论】:

【解决方案6】:

谈到本土解决方案,前段时间我写了一个小 program 来做到这一点 - 将写为 \p{...} 的 unicode 类别转换为从 unicode specification (v.5.0. 0)。仅支持类别(例如:LZs),并且仅限于 BMP。我把它贴在这里以防有人觉得它有用(尽管 Oniguruma 似乎确实是一个更好的选择)。

示例用法:

>>> from unicode_hack import regex
>>> pattern = regex(r'^\\p{Lu}(\\p{L}|\\p{N}|_)*')
>>> print pattern.match(u'疂_1+2').group(0)
疂_1
>>>

这是source。还有一个JavaScript version,用的是同样的数据。

【讨论】:

  • 不错,尽管您在代码中使用手工制作的文字。如果这些文字是从规范的某种文本形式生成的,那就太好了。或来自 unicodedata (docs.python.org/library/unicodedata.html#module-unicodedata)。您可能会遍历所有有效的 unicode 代码点并通过 unicodedata.category() 运行它们,并使用输出来填充地图...
  • 感谢您的提示,我可能有一天会实现。上面的代码首先是为 JavaScript 创建的(当时几乎没有合理的替代方案),然后移植到 Python。我在规范上运行了一些正则表达式并完成了一个一次性脚本,但我同意一个可重复的过程会更好,所以我可以让它保持最新。
  • 我已经破解了一个快速函数,可以动态构建地图(只有字符列表作为值): def unicats(maxu): m = defaultdict(list) for i in range(maxu ): try: cat=unicodedata.category(unichr(i)) except: cat=None if cat: m[cat].append(i) return mm=unicats(10FFFF) 注意有些类别变得非常大(例如 len (m['Cn']) == 873882)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-24
  • 1970-01-01
  • 2014-01-17
  • 2017-01-24
  • 2018-08-12
相关资源
最近更新 更多