Question Gamme de caractères UTF-8 en C ++ 11 Regex


Cette question est une extension de Les expressions régulières C ++ 11 fonctionnent-elles avec les chaînes UTF-8?

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

Le programme est compilé sur Mac Mountain Lion avec clang++ avec les options suivantes:

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

Le code ci-dessus fonctionne. Ceci est une regex de gamme standard "[一-龠々〆ヵヶ]" pour faire correspondre n'importe quel caractère japonais ou kanji japonais. Cela fonctionne en Javascript et Ruby, mais je ne peux pas sembler obtenir des plages fonctionnant en C ++ 11, même en utilisant une version similaire [\u4E00-\u9fa0]. Le code ci-dessous ne correspond pas à la chaîne.

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

La modification des paramètres régionaux n’a pas aidé non plus. Des idées?

MODIFIER

Donc, j'ai trouvé que toutes les gammes fonctionnent si vous ajoutez un + jusqu'à la fin. Dans ce cas [一-龠々〆ヵヶ]+, mais si vous ajoutez {1}  [一-龠々〆ヵヶ]{1} ça ne marche pas. De plus, il semble dépasser ses limites. Il ne correspondra pas aux caractères latins, mais il correspondra  lequel est \u306f et  lequel est \u3041. Ils sont tous deux en dessous \u4E00

nhahtdh a également suggéré regex_search qui fonctionne également sans ajout + mais il se heurte toujours au même problème que ci-dessus en tirant des valeurs hors de sa portée. Joué un peu avec les locales. Mark Ransom suggère de traiter la chaîne UTF-8 comme un ensemble d'octets idiot, je pense que c'est peut-être ce qu'elle fait.

Poussant plus loin la théorie selon laquelle l’UTF-8 est en train de s’embrouiller, comment, [a-z]{1} et [a-z]+ allumettes a, mais, seulement [一-龠々〆ヵヶ]+ correspond à l'un des caractères, pas [一-龠々〆ヵヶ]{1}.


30
2018-04-08 15:22


origine


Réponses:


Encodé en UTF-8, la chaîne "[一-龠々〆ヵヶ]" est égal à celui-ci: "[\xe4\xb8\x80-\xe9\xbe\xa0\xe3\x80\x85\xe3\x80\x86\xe3\x83\xb5\xe3\x83\xb6]". Et ce n'est pas la droïde classe de personnage que vous recherchez.

La classe de personnage que vous recherchez est celle qui comprend:

  • tout caractère dans la plage U + 4E00..U + 9FA0; ou
  • l'un des caractères 々, 〆, ヵ, ヶ.

La classe de caractères que vous avez spécifiée est celle qui comprend:

  • l'un des "caractères" \ xe4 ou \ xb8; ou
  • tout "caractère" dans la plage \ x80 .. \ xe9; ou
  • n'importe lequel des "caractères" \ xbe, \ xa0, \ xe3, \ x80, \ x85, \ xe3 (encore), \ x80 (encore), \ x86, \ xe3 (encore), \ x83, \ xb5, \ xe3 (encore), \ x83 (encore), \ xb6.

Messy n'est-ce pas? Voyez-vous le problème?

Cela ne correspondra pas aux caractères "latins" (que je suppose que vous voulez dire des choses comme a-z) car dans UTF-8, ils utilisent tous un seul octet inférieur à 0x80, et aucun de ceux-ci ne se trouve dans cette classe.

Il ne correspondra pas "中" Soit parce que "中" a trois "caractères" et votre expression régulière ne correspond qu’à un seul "caractère" de cette longue liste. Essayer assert(std::regex_match("中", std::regex("..."))) et vous allez voir.

Si vous ajoutez un + ça marche parce que "中" a trois de ces "personnages" dans votre longue liste étrange, et maintenant votre regex correspond à un ou plusieurs.

Si vous ajoutez plutôt {1} cela ne correspond pas parce que nous sommes revenus à la correspondance de trois "personnages" contre un.

Incidemment "中" allumettes "中" car nous faisons correspondre les trois "caractères" avec les trois mêmes "caractères" dans le même ordre.

Que le regex avec + va effectivement correspondre à des choses indésirables, car il ne se soucie pas de l'ordre. Tout caractère pouvant être créé à partir de cette liste d'octets dans UTF-8 correspondra. Il correspondra "\xe3\x81\x81"(ぁ U + 3041) et il correspondra même à l'entrée UTF-8 invalide comme "\xe3\xe3\xe3\xe3".

Le plus gros problème est que vous utilisez une bibliothèque regex qui n'a même pas de support de niveau 1 pour Unicode, le strict minimum requis. Il libère des octets et votre précieux petit regex ne peut rien y faire.

Et le problème encore plus important est que vous utilisez un ensemble de caractères codés en dur pour spécifier "n'importe quel caractère japonais ou kanji japonais". Pourquoi ne pas utiliser la propriété Script Unicode pour cela?

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

Ah oui, cela ne fonctionnera pas avec les regex C ++ 11. Pendant un moment, j'ai presque oublié que ceux-ci sont pires qu'inutiles avec Unicode.

Alors, que devrais-tu faire?

Vous pourriez décoder votre entrée dans un std::u32string et utilise char32_t partout pour la correspondance. Cela ne vous donnerait pas ce gâchis, mais vous seriez toujours des plages de calcul et des exceptions lorsque vous voulez dire "un ensemble de caractères partageant une certaine propriété".

Je vous recommande d’oublier les regex C ++ 11 et d’utiliser une bibliothèque d’expressions régulières qui supporte au moins le niveau Unicode minimum, comme celui de ICU.


33
2018-04-09 07:21