<regex> возникли проблемы с кириллицей c символов - PullRequest
3 голосов
/ 03 марта 2020

Я пытаюсь использовать стандартную библиотеку <regex> для сопоставления с некоторыми словами кириллицы c:

  // 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() << "#";
  }

Однако это не похоже на работу. Приведенный выше код приводит к следующему:

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

вместо ожидаемого:

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

Код вышеприведенного символа '�' \321.

Я проверил регулярное выражение, которое использовал с grep, и оно работает как положено. Моя локаль en_US.UTF-8. И G CC, и Clang дают один и тот же результат.

Что-то мне не хватает? Есть ли способ «приручить» <regex>, чтобы он работал с символами кириллицы c?

Ответы [ 2 ]

3 голосов
/ 03 марта 2020

Для диапазонов, таких как А-Я, для правильной работы необходимо использовать std::regex::collate

Константы
...
collate Символьные диапазоны вида «[ab]» будут чувствительны к локали.

Изменение регулярного выражения на

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

дает ожидаемый результат.


В зависимости от кодировки вашего исходного файла может потребоваться префикс строки регулярного выражения с u8

std::regex re{u8"[А-Яа-яЁё]+", std::regex::collate};
1 голос
/ 03 марта 2020

кириллицы c буквы представлены в UTF-8 в виде многобайтовых последовательностей. Поэтому одним из способов решения этой проблемы является использование "широкой" версии string, называемой wstring. Другие функции и типы, работающие с широкими символами, также необходимо заменить их «многобайтовой» версией, обычно это делается путем добавления 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 для создания регулярных выражений locale- чувствителен к обычным строкам, см. этот ответ @OlafDietsche для деталей. Эта топика c позволит пролить свет на то, какое решение может быть более предпочтительным в ваших обстоятельствах. (Оказывается, в моем случае collate была лучшей идеей!)
...