Диапазон символов UTF-8 в С++ 11 Regex

Этот вопрос является расширением Выполняют ли регулярные выражения С++ 11 строки UTF-8?

#include <regex>  
if (std::regex_match ("中", std::regex("中") ))  // "\u4e2d" also works
  std::cout << "matched\n";

Программа скомпилирована на Mac Mountain Lion с помощью clang++ со следующими параметрами:

clang++ -std=c++0x -stdlib=libc++

Выполняется код выше. Это стандартное регулярное выражение "[一-龠々〆ヵヶ]" для сопоставления любого японского иероглифа или иероглифа. Он работает в Javascript и Ruby, но я не могу заставить диапазоны работать на С++ 11, даже используя аналогичную версию [\u4E00-\u9fa0]. Код ниже не соответствует строке.

if (std::regex_match ("中", std::regex("[一-龠々〆ヵヶ]")))
  std::cout << "range matched\n";

Изменение языкового стандарта также не помогло. Любые идеи?

ИЗМЕНИТЬ

Итак, я обнаружил, что все диапазоны работают, если вы добавите + в конец. В этом случае [一-龠々〆ヵヶ]+, но если вы добавите {1} [一-龠々〆ヵヶ]{1}, это не сработает. Более того, он, кажется, преодолевает его границы. Он не будет соответствовать латинским символам, но он будет соответствовать , который равен \u306f и , который равен \u3041. Оба они лежат ниже \u4E00

nhahtdh также предложил regex_search, который также работает без добавления +, но он по-прежнему сталкивается с той же проблемой, что и выше, вытягивая значения за пределами своего диапазона. Играл с локалями немного. Марк Рэнсом предлагает, чтобы он рассматривал строку UTF-8 как немой набор байтов, я думаю, что это возможно, что он делает.

Дальнейшее продвижение теории о том, что UTF-8 начинает смешиваться, как [a-z]{1} и [a-z]+ соответствует a, но только [一-龠々〆ヵヶ]+ соответствует любому из символов, а не [一-龠々〆ヵヶ]{1}.

Ответ 1

В кодировке UTF-8 строка "[一-龠々〆ヵヶ]" равна этой: "[\xe4\xb8\x80-\xe9\xbe\xa0\xe3\x80\x85\xe3\x80\x86\xe3\x83\xb5\xe3\x83\xb6]". И это не класс символов droid, который вы ищете.

Класс символов, который вы ищете, - это тот, который включает в себя:

  • любой символ в диапазоне U + 4E00..U + 9FA0; или
  • любой из символов 々, 〆, ヵ, ヶ.

Указанный вами класс символов таков:

  • любой из "символов" \xe4 или \xb8; или
  • любой "символ" в диапазоне \x80..\xe9; или
  • любой из "символов" \xbe,\xa0,\xe3,\x80,\x85,\xe3 (снова),\x80 (снова),\x86,\xe3 (снова),\x83,\xb5,\xe3 (снова),\x83 (снова),\xb6.

Неплохо, не так ли? Вы видите проблему?

Это не будет соответствовать "латинским" символам (что, я полагаю, вы имеете в виду такие вещи, как a-z), потому что в UTF-8 все используют один байт ниже 0x80, и ни один из них не находится в этом беспорядочном символьном классе.

Он не будет соответствовать "中" либо потому, что "中" имеет три "символа", и ваше регулярное выражение совпадает только с одним "символом" из этого странного длинного списка. Попробуйте assert(std::regex_match("中", std::regex("..."))), и вы увидите.

Если вы добавили +, это работает, потому что "中" имеет три из этих "символов" в вашем странном длинном списке, и теперь ваше регулярное выражение соответствует одному или нескольким.

Если вы добавите {1}, это не соответствует, потому что мы вернулись к сопоставлению трех "символов" с одним.

Кстати, "中" соответствует "中", потому что мы сопоставляем три "символа" с теми же тремя "символами" в том же порядке.

Что регулярное выражение с + действительно будет соответствовать некоторым нежелательным вещам, потому что оно не заботится о порядке. Любой символ, который может быть сделан из этого списка байтов в UTF-8, будет соответствовать. Он будет соответствовать "\xe3\x81\x81" (ぁ U + 3041), и он будет даже соответствовать недопустимому входу UTF-8, например "\xe3\xe3\xe3\xe3".

Большая проблема заключается в том, что вы используете библиотеку регулярных выражений, которая даже не поддерживает поддержку уровня 1 для Unicode, минимальный минимум. Он мутит байты и не так много может сделать ваше дорогое крошечное регулярное выражение.

И еще большая проблема заключается в том, что вы используете жестко заданный набор символов для указания "любого японского иероглифа или иероглифа". Почему бы не использовать свойство Unicode Script для этого?

R"(\p{Script=Han})"

О, верно, это не будет работать с регулярными выражениями С++ 11. На мгновение я почти забыл, что это раздражительно хуже, чем бесполезно с Unicode.

Итак, что вы должны делать?

Вы можете декодировать свой ввод в std::u32string и использовать char32_t для соответствия. Это не даст вам этого беспорядка, но вы по-прежнему будете иметь диапазоны жесткого кодирования и исключения, если вы имеете в виду "набор символов, которые имеют определенное свойство".

Я рекомендую вам забыть о регулярных выражениях С++ 11 и использовать некоторую библиотеку регулярных выражений с минимальной поддержкой Unicode уровня 1, например, в ICU.