【问题标题】:Why is there no definition for std::regex_traits<char32_t> (and thus no std::basic_regex<char32_t>) provided?为什么没有提供 std::regex_traits<char32_t> 的定义(因此没有 std::basic_regex<char32_t>)?
【发布时间】:2016-02-15 23:42:37
【问题描述】:

我想在 UTF-32 代码点上使用正则表达式,发现 reference 声明 std::regex_traits 必须由用户定义,以便完全可以使用 std::basic_regex。未来似乎没有计划对此进行任何更改。

  1. 为什么会这样?

  2. 这是否与 Unicode 表示组合代码点必须被视为等于单个代码点表示的事实有关(如变音符号“ä”表示为单个代码点或用 a 和点表示作为两个独立的)?

  3. 考虑到仅支持单码点字符的简化,是否可以轻松定义此特征,或者这是否不重要或需要进一步限制?

【问题讨论】:

    标签: c++ regex c++11


    【解决方案1】:

    我刚刚发现了一个支持char32_t的正则表达式实现:http://www.akenotsuki.com/misc/srell/en/

    它模仿std::regex API 并受 BSD 许可。

    【讨论】:

      【解决方案2】:
      1. 正则表达式匹配的某些方面是区域设置感知的,因此std::regex_traits 对象包含或引用std::locale 对象的实例。 C++ 标准库仅提供charwchar_t 字符的语言环境,因此char32_t 没有标准语言环境(除非它恰好与wchar_t 相同),并且此限制会延续到正则表达式中。

      2. 您的描述不准确。 Unicode 定义了两个字符串之间的规范等价关系,它基于使用 NFC 或 NFD 对两个字符串进行标准化,然后逐个代码点比较标准化值。它没有将规范等价简单地定义为代码点和代码点序列之间的等价,因为不能简单地逐个字符地进行规范化。规范化可能需要将组成字符重新排序为规范顺序(在规范(de)组合之后)。因此,它不容易适应 C++ 语言环境转换模型,通常是单字符。

        C++ 标准库没有实现任何 Unicode 规范化算法;在 C++ 中,与在许多其他语言中一样,两个字符串 L"\u00e4" (ä) 和 L"\u0061\u0308" (ä) 将比较不同,尽管它们在规范上是等效的,并且在人类读者看来就像相同的字素。 (在我写这个答案的机器上,这两个字素的呈现略有不同;如果你仔细观察,你会发现第二个中的变音符号稍微偏离了它的视觉最佳位置。这违反了 Unicode要求规范等效的字符串具有完全相同的呈现。)

        如果要检查两个字符串的规范等效性,则需要使用 Unicode 规范化库。不幸的是,C++ 标准库不包含任何这样的 API。你可以看看ICU(其中还包括Unicode-aware regex matching)。

        在任何情况下,正则表达式匹配——在 C++ 标准中指定的范围内——不会规范化目标字符串。 Unicode Technical Report on regular expressions 允许这样做,它建议将目标字符串显式规范化为某种规范化形式,并将模式写入规范化为该形式的字符串:

        对于大多数功能齐全的正则表达式引擎,在规范等价下进行匹配是相当困难的,这可能涉及字符的重新排序、拆分或合并……实际上,正则表达式 API 并未设置为匹配部分字符或处理不连续的选择。还有许多其他边缘情况……但是,构造与 NFD(或 NFKD)文本匹配的模式是可行的。这可以通过以下方式完成:

        • 将要匹配的文本放入定义的规范化形式(NFD 或 NFKD)中。
        • 让用户设计正则表达式模式以匹配定义的规范化形式。例如,模式不应包含不会以该规范化形式出现的字符,也不应包含不会出现的序列。
        • 照常逐个代码点应用匹配算法。
      3. 创建std::regex_traitschar32_t 特化的大部分工作将是创建char32_t 语言环境对象。我从来没有尝试过做这些事情;我怀疑这需要相当多的关注细节,因为有很多奇怪的极端情况。


      C++ 标准对正则表达式匹配的细节有些模糊,将细节留给关于正则表达式每种风格的外部文档(并且没有完整解释如何将这些外部规范应用于每个字符类型以外的字符类型风味是指定的)。然而,可以推断出匹配是逐个字符的事实。例如,在第 28.3 节,Requirements [re.req] 中,表 136 包括负责逐字符等效算法的语言环境方法:

      表达式:v.translate(c) 返回类型:X::char_type 断言:返回一个字符,使得对于任何被认为等同于c 的字符d 然后v.translate(c) == v.translate(d)

      同样,在对默认“Modified ECMAScript”风格(第 28.13 节)的正则表达式匹配的描述中,标准描述了正则表达式引擎如何匹配两个字符(一个在模式中,一个在目标中):(第 14.1 段):

      在将正则表达式有限状态机与字符序列匹配期间,使用以下规则比较两个字符 cd

      1. 如果(flags() &amp; regex_constants::icase)这两个字符相等如果traits_inst.translate_nocase(c) == traits_inst.translate_nocase(d);

      2. 否则,如果flags() &amp; regex_constants::collate两个字符相等如果traits_inst.translate(c) == traits_inst.translate(d);

      3. 否则,如果c == d,则两个字符相等。

      【讨论】:

      • 关于 #2:根据以下问题的答案,如果由语言环境指定,则 unicode 等效性实际上会受到影响。答案还列出了受语言环境影响的其他正则表达式行为,因此与 #3 相关:stackoverflow.com/questions/9043712/…
      • @Ident:并非您链接的所有答案都是正确的。语言环境会影响(单个)字符与特殊字符类([:class:][.class.][=class=])和字符范围([a-z])的匹配。如果设置了1casecollate 标志,它还会影响单个字符相互匹配。但是(至少在标准的正则表达式方言中),比较总是与单个CharT;目标字符串中的多字符代码不会被重新解释为单个代码点...
      • 好的,谢谢你的回答,最后一个问题,假设我不想支持正确的排序,只是想按字符的数字表示按非字典顺序排序,并且通常希望一切都能像“C”语言环境在字符类等方面确实如此,这是否容易实现,或者您在最初的答案中提到的那些极端情况是否会发挥作用?为此,我可以主要将 cha32_t regex_traits 包裹在 char regex_traits 周围吗?
      • ...将大部分评论移到答案中。
      • @Ident:如果你的wchar_t 是 32 位,那么一切都应该很简单。如果wchar_t 仅为 16 位,则其语言环境可能无法正确处理 BMP 之外的 unicode 字符的字符类型。你必须尝试一下。如果你安装了 ICU,你可以将几乎所有的工作都交给 ICU 函数,但是你也可以使用 ICU 正则表达式 API 而不是 C++ 标准库。
      猜你喜欢
      • 2014-11-28
      • 1970-01-01
      • 2015-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-21
      • 1970-01-01
      相关资源
      最近更新 更多