<regex> 遇到西里尔字符问题

<regex> having trouble with Cyrillic characters

我正在尝试使用标准 <regex> 库来匹配一些西里尔文字:

  // This is a UTF-8 file.
  std::locale::global(std::locale("en_US.UTF-8"));

  string s {"Каждый охотник желает знать где сидит фазан."};
  regex re {"[А-Яа-яЁё]+"};

  for (sregex_iterator it {s.begin(), s.end(), re}, end {}; it != end; it++) {
    cout << it->str() << "#";
  }

但是,这似乎不起作用。上面的代码产生以下结果:

  Кажд�#й#о�#о�#ник#желае�#зна�#�#где#�#иди�#�#азан#

而不是预期的:

  Каждый#охотник#желает#знать#где#сидит#фазан

上面'��'符号的代码是1.

我检查了我在 grep 中使用的正则表达式,它按预期工作。我的语言环境是 en_US.UTF-8。 GCC 和 Clang 产生相同的结果。

有什么我遗漏的吗?有没有办法 "tame" <regex> 所以它可以与西里尔字符一起使用?

西里尔字母在 UTF-8 中表示为多字节序列。因此,处理该问题的一种方法是使用名为 wstringstring 的 "wide" 版本。其他使用宽字符的函数和类型也需要替换为它们的 "multibyte-conscious" 版本,通常这是通过在它们的名称前加上 w 来完成的。这有效:

std::locale::global(std::locale("en_US.UTF-8"));

wstring s {L"Каждый охотник желает знать где сидит фазан."};
wregex re {L"[А-Яа-яЁё]+"};

for (wsregex_iterator it {s.begin(), s.end(), re}, end {}; it != end; it++) {
  wcout << it->str() << "#";
}

输出:

Каждый#охотник#желает#знать#где#сидит#фазан#

(感谢@JohnDing 推介此解决方案。)


另一种解决方案是使用 regex::collate 使正则表达式对普通字符串敏感,请参阅 by @OlafDietsche for details. This topic 将阐明在您的情况下哪种解决方案更可取。 (结果在我的案例中 collate 是一个更好的主意!)

要使 А-Я 这样的范围正常工作,您必须使用 std::regex::collate

Constants
...
collate Character ranges of the form "[a-b]" will be locale sensitive.

将正则表达式更改为

std::regex re{"[А-Яа-яЁё]+", std::regex::collate};

给出了预期的结果。


根据源文件的编码,您可能需要在正则表达式字符串前加上 u8

std::regex re{u8"[А-Яа-яЁё]+", std::regex::collate};