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

Этот вопрос является расширением работы регулярных выражений C++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, но я не могу заставить работать диапазоны в C++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]", И это не тот класс персонажей- дроидов, которого вы ищете.

Класс персонажа, который вы ищете, включает в себя:

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

Указанный вами класс символов включает в себя:

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

Грязный не так ли? Ты видишь проблему?

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

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

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

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

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

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

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

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

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

Ах да, это не будет работать с регулярными выражениями C++11. На мгновение я чуть не забыл, что они раздражают хуже, чем бесполезные в Юникоде.

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

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

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

Другие вопросы по тегам