ICU 库、Rust 和 PCRE 正则表达式匹配的不同结果(https://regexr.com/)

Dam*_*ons -1 regex icu rust

这是我使用的模式:

\n
"\\w+|[^\\w\\s]+"\n
Run Code Online (Sandbox Code Playgroud)\n

当我匹配字符串“abc.efg”和“\xe6\x88\xa6\xe5\xa0\xb4\xe3\x81\xae\xe3\x83\xb4\xe3\x82\xa1\xe3\x83\xab\xe3\ x82\xad\xe3\x83\xa5\xe3\x83\xaa\xe3\x82\xa23" 在https://regexr.com/中使用 PCRE ,\nit 给我这样的结果:

\n
"abc" "." "efg" => 3 parts\n\n"\xe6\x88\xa6\xe5\xa0\xb4\xe3\x81\xae\xe3\x83\xb4\xe3\x82\xa1\xe3\x83\xab\xe3\x82\xad\xe3\x83\xa5\xe3\x83\xaa\xe3\x82\xa2" "3" => 2 parts\n
Run Code Online (Sandbox Code Playgroud)\n

看起来是对的。

\n

但是当我像这样使用 icu 时:

\n
    //std::string ldata = "abc.efg";\n    std::string ldata = "\xe6\x88\xa6\xe5\xa0\xb4\xe3\x81\xae\xe3\x83\xb4\xe3\x82\xa1\xe3\x83\xab\xe3\x82\xad\xe3\x83\xa5\xe3\x83\xaa\xe3\x82\xa23";\n    std::string m_regex = "\\\\w+|[^\\\\w\\\\s]+";\n    UErrorCode         status = U_ZERO_ERROR;\n    icu::RegexMatcher  matcher(m_regex.c_str(), 0, status);\n    icu::StringPiece   data((char*)ldata.data(), ldata.length());\n    icu::UnicodeString input = icu::UnicodeString::fromUTF8(data);\n    matcher.reset(input);\n   \n    \n    int count = 0;\n    while (matcher.find(status) && U_SUCCESS(status))\n    {\n        auto start_index = matcher.start(status);\n        auto end_index   = matcher.end(status);\n        count++;   \n    }\n
Run Code Online (Sandbox Code Playgroud)\n

输入字符串“abc.efg”给我:

\n
"abc" "." "efg" => 3 parts\n
Run Code Online (Sandbox Code Playgroud)\n

但输入字符串“\xe6\x88\xa6\xe5\xa0\xb4\xe3\x81\xae\xe3\x83\xb4\xe3\x82\xa1\xe3\x83\xab\xe3\x82\xad\xe3\ x83\xa5\xe3\x83\xaa\xe3\x82\xa23”给我:

\n
"\xe6\x88\xa6\xe5\xa0\xb4\xe3\x81\xae\xe3\x83\xb4\xe3\x82\xa1\xe3\x83\xab\xe3\x82\xad\xe3\x83\xa5\xe3\x83\xaa\xe3\x82\xa23" => 1 part\n
Run Code Online (Sandbox Code Playgroud)\n

当我像这样使用 Rust 时:

\n
impl Pattern for &Regex {\n    fn find_matches(&self, inside: &str) -> Result<Vec<(Offsets, bool)>> {\n        if inside.is_empty() {\n            return Ok(vec![((0, 0), false)]);\n        }\n\n        let mut prev = 0;\n        let mut splits = Vec::with_capacity(inside.len());\n        for m in self.find_iter(inside) {\n            if prev != m.start() {\n                splits.push(((prev, m.start()), false));\n            }\n            splits.push(((m.start(), m.end()), true));\n            prev = m.end();\n        }\n        if prev != inside.len() {\n            splits.push(((prev, inside.len()), false))\n        }\n        Ok(splits)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

输入字符串“abc.efg”给我:

\n
"abc" "." "efg" => 3 parts\n
Run Code Online (Sandbox Code Playgroud)\n

但输入字符串“\xe6\x88\xa6\xe5\xa0\xb4\xe3\x81\xae\xe3\x83\xb4\xe3\x82\xa1\xe3\x83\xab\xe3\x82\xad\xe3\ x83\xa5\xe3\x83\xaa\xe3\x82\xa23”给我:

\n
"\xe6\x88\xa6\xe5\xa0\xb4\xe3\x81\xae\xe3\x83\xb4\xe3\x82\xa1\xe3\x83\xab\xe3\x82\xad\xe3\x83\xa5\xe3\x83\xaa\xe3\x82\xa23" => 1 part\n
Run Code Online (Sandbox Code Playgroud)\n

为什么 ICU 和 Rust 匹配 "\xe6\x88\xa6\xe5\xa0\xb4\xe3\x81\xae\xe3\x83\xb4\xe3\x82\xa1\xe3\x83\xab\xe3\x82\xad\xe3 \x83\xa5\xe3\x83\xaa\xe3\x82\xa23" 给出与 PCRE 不同的结果(https://regexr.com/

\n

看起来“\xe6\x88\xa6\xe5\xa0\xb4\xe3\x81\xae\xe3\x83\xb4\xe3\x82\xa1\xe3\x83\xab\xe3\x82\xad\xe3\x83 \xa5\xe3\x83\xaa\xe3\x82\xa23" 应匹配为 2 部分。

\n

Mas*_*inn 5

ICU 和正则表达式默认使用 unicode 语义,这意味着\\w它们使用“单词字符”的 unicode 感知定义。

\n

对于正则表达式来说

\n
[\\p{Alphabetic}\\p{M}\\p{Nd}\\p{Pc}\\p{Join_Control}]\n
Run Code Online (Sandbox Code Playgroud)\n

对于 ICU 来说

\n
[\\p{Alphabetic}\\p{Mark}\\p{Decimal_Number}\\p{Connector_Punctuation}\\u200c\\u200d]\n
Run Code Online (Sandbox Code Playgroud)\n

其中每个 tr44Alphabetic是:

\n
Lowercase + Uppercase + Lt + Lm + Lo + Nl + Other_Alphabetic\n
Run Code Online (Sandbox Code Playgroud)\n

CJK 字符通常被分类为“字母、其他”(Lo),因此属于\\wunicode 感知分类的一部分。显然,“3”也是如此。因此只有一个组,因为它们都匹配得\\w+很好。

\n

PCRE 默认情况下不使用 unicode 语义1因此它不处理 "\xe6\x88\xa6\xe5\xa0\xb4\xe3\x81\xae\xe3\x83\xb4\xe3\x82\xa1\xe3\x83\ xab\xe3\x82\xad\xe3\x83\xa5\xe3\x83\xaa\xe3\x82\xa2" 作为字母。

\n

regex 支持非 unicode 匹配(使用基于字节的引擎或标志(?-u:)),我不知道 ICU 是否支持,尽管我相当怀疑它,因为它完全违背了这一点。

\n

如果您想要特定的 ASCII 匹配,只需提出要求即可。

\n

还是您误解了其中的\\w内容并认为它不包含数字?因此 PCRE 匹配“\xe6\x88\xa6\xe5\xa0\xb4\xe3\x81\xae\xe3\x83\xb4\xe3\x82\xa1\xe3\x83\xab\xe3\x82\xad\xe3 \x83\xa5\xe3\x83\xaa\xe3\x82\xa2”到\\w+和“3”到[^\\w\\s]+?因为它的作用恰恰相反。

\n
\n

1:PCRE2_UCP允许启用 unicode 语义

\n

  • 对于 PCRE2,您可以通过启用“PCRE2_UCP”选项(以及“PCRE2_UTF”)使其行为与 Rust 正则表达式和 ICU 相同。这将使 `\w` 能够识别 Unicode。请参阅:https://www.pcre.org/current/doc/html/pcre2pattern.html (3认同)
  • 如果你想得到与ICU相同的结果,你确实可以写出字符类。但 ICU(和 Rust 的正则表达式箱)都支持字符类集操作(PCRE2 不支持)。因此,您可以编写“[\w&amp;&amp;\p{ascii}]”来获取仅 ASCII 的变体。您还可以执行“[[^\w\s]&amp;&amp;\p{ascii}]”。请注意,后者仅限于匹配 ASCII,而 Rust 的正则表达式板条箱和 PCRE2 会将 `[^\w\s]` 视为匹配不在 `[\w\s]` 中的任何单个字节,包括无效的 UTF-8。 (2认同)