2013-04-08 6 views
29

Diese Frage ist eine Erweiterung von Do C++11 regular expressions work with UTF-8 strings?Bereich von UTF-8-Zeichen in C++ 11 Regex

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

Das Programm auf dem Mac Mountain Lion mit clang++ mit den folgenden Optionen kompiliert wird:

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

Der obige Code funktioniert. Dies ist ein Standardbereich Regex "[一-龠々〆ヵヶ]" für den Abgleich von japanischen Kanji oder chinesischen Schriftzeichen. Es funktioniert in Javascript und Ruby, aber ich kann nicht scheinen Bereiche in C++ 11 arbeiten, auch mit einer ähnlichen Version [\u4E00-\u9fa0]. Der folgende Code stimmt nicht mit der Zeichenfolge überein.

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

Das Ändern der Ländereinstellung hat auch nicht geholfen. Irgendwelche Ideen?

EDIT

So fand ich, dass alle Bereiche, wenn Sie eine + bis zum Ende hinzuzufügen. In diesem Fall [一-龠々〆ヵヶ]+, aber wenn Sie {1}[一-龠々〆ヵヶ]{1} hinzufügen funktioniert es nicht. Außerdem scheint es seine Grenzen zu überschreiten. Es wird nicht lateinischen Zeichen entsprechen, aber es wird entsprechen, das ist \u306f und das \u3041 ist. Sie liegen beide unter \u4E00

nhahtdh schlug auch regex_search vor, das auch funktioniert, ohne + hinzuzufügen, aber es läuft immer noch in das gleiche Problem wie oben, indem es Werte außerhalb seines Bereichs zieht. Gespielt mit den Locales auch ein bisschen. Mark Ransom schlägt vor, dass es die UTF-8-Zeichenfolge als eine dumme Menge von Bytes behandelt, ich denke, dass dies möglicherweise das ist, was sie tut.

schieben Weitere die Theorie, dass UTF-8 ist etwas, wie durcheinander bekommen, [a-z]{1} und [a-z]+a Matches, aber nur [一-龠々〆ヵヶ]+ Matches eines der Zeichen, nicht [一-龠々〆ヵヶ]{1}.

+0

Was ist der Compiler? – nhahtdh

+0

clang ++ -std = C++ 0x -stdlib = libC++ auf Mac Mountain Lion – MCH

+0

Einige Experimente und ich habe eine Lösung gefunden, fügen Sie '+' am Ende des Bereichs – MCH

Antwort

30

In UTF-8 codiert ist die Zeichenfolge "[一-龠々〆ヵヶ]" gleich dieser: "[\xe4\xb8\x80-\xe9\xbe\xa0\xe3\x80\x85\xe3\x80\x86\xe3\x83\xb5\xe3\x83\xb6]". Und das ist nicht die Droid Charakterklasse, die Sie suchen.

Die Zeichenklasse Sie suchen, ist derjenige, der folgendes beinhaltet:

  • jedes Zeichen im Bereich U + 4E00..U + 9FA0; oder
  • eines der Zeichen 々, 〆, ヵ, ヶ.

der Zeichenklasse Sie ist derjenige angegeben, der folgendes beinhaltet:

  • einem der "Zeichen" \ XE4 oder \ XB8; oder
  • irgendein "Zeichen" im Bereich \ x80 .. \ xe9; oder
  • irgendeines der "Zeichen" \ xbe, \ xa0, \ xe3, \ x80, \ x85, \ xe3 (wieder), \ x80 (wieder), \ x86, \ xe3 (wieder), \ x83, \ xb5, \ xe3 (wieder), \ x83 (wieder), \ xb6.

Messy ist es nicht? Siehst du das Problem?

Das passt nicht zu "lateinischen" Zeichen (ich nehme an, Sie meinen Dinge wie a-z), weil in UTF-8 alle ein einzelnes Byte unter 0x80 verwenden und keines davon in dieser unordentlichen Zeichenklasse ist.

Es passt nicht "中" entweder weil "中" hat drei "Zeichen", und Ihre Regex stimmt nur ein "Zeichen" aus dieser seltsamen langen Liste. Versuchen Sie assert(std::regex_match("中", std::regex("..."))) und Sie werden sehen.

Wenn Sie eine + hinzufügen funktioniert es, weil "中" drei dieser "Zeichen" in Ihrer seltsamen langen Liste hat, und jetzt Ihre Regex entspricht einem oder mehreren.

Wenn Sie stattdessen {1} hinzufügen, stimmt es nicht überein, da wir wieder drei übereinstimmende "Zeichen" gegen eins haben.

Übrigens "中" entspricht "中", weil wir die drei "Zeichen" mit den gleichen drei "Zeichen" in der gleichen Reihenfolge übereinstimmen.

Die Regex mit + wird tatsächlich einige unerwünschte Dinge übereinstimmen, weil es Ordnung nicht kümmert. Jedes Zeichen, das aus dieser Liste von Bytes in UTF-8 erstellt werden kann, wird übereinstimmen. Es entspricht "\xe3\x81\x81" (ぁ U + 3041) und es wird sogar ungültige UTF-8-Eingabe wie "\xe3\xe3\xe3\xe3" übereinstimmen.

Das größere Problem ist, dass Sie eine Regex-Bibliothek verwenden, die nicht einmal Level 1-Unterstützung für Unicode, das absolute Minimum benötigt. Es munges Byte und es gibt nicht viel, das Ihre kostbare kleine Regex darüber tun kann.

Und das noch größere Problem ist, dass Sie eine fest codierte Menge von Zeichen verwenden, um "jedes japanische Kanji oder chinesische Schriftzeichen" anzugeben. Warum nicht die Unicode Script-Eigenschaft dafür verwenden?

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

Oh richtig, das wird nicht mit C++ 11 Regexes arbeiten. Für einen Moment hätte ich fast vergessen, dass diese ärgerlicherweise schlimmer sind als nutzlos mit Unicode.

Was sollten Sie tun?

Sie könnten Ihre Eingabe in eine std::u32string decodieren und für die Anpassung verwenden. Das würde dir nicht diese Unordnung bringen, aber du würdest immer noch Bereiche und Ausnahmen fest codieren, wenn du "eine Menge von Charakteren meinst, die eine bestimmte Eigenschaft teilen".

Ich empfehle Ihnen, über C++ 11 Regexes zu vergessen und eine Bibliothek für reguläre Ausdrücke zu verwenden, die die minimale Unicode-Unterstützung der Ebene 1 hat, wie die in ICU.

+0

Vielen Dank Martinho, das ist ein sehr informativer Beitrag. Gibt mir und noch besseres Verständnis von UTF-8 und regulären Ausdrücken. Wie auch immer, ich habe Regex in diesem Projekt aufgegeben, da ich nur wissen muss, ob eine Glyphe zu einem bestimmten Bereich gehört, und sie dann in diesen Bereich taggen, so dass Hardcoding eine schnelle und einfache Lösung ist. Ich dachte, Regexes wären eine einfache und elegante Lösung, aber ich fand, dass dies für C++ 11 nicht gilt. – MCH

+8

@MCH Ja, ich denke, die ICU für ein winziges Streichholz zu packen könnte zu viel sein. Wenn Sie Unicode und Regexes verwenden wollen, ist Perl die einzige Sprache, die es ernst nimmt. Es ist ein trauriger Zustand, aber wir haben es. Persönlich denke ich '' ist mehr Müll in der stdlib. Es ist 2013 und das Vortäuschen, dass Unicode nicht existiert, ist witzig und trägt nur dazu bei, dass der Umgang mit Unicode zu schmerzhaft ist (Tipp: Wenn dein Hammer keinen Kopf hat, wirst du es schwer haben Nägel zu fahren). –

+0

Verwenden Sie stattdessen wregex und verwenden Sie http://utfcpp.sourceforge.net/ oder setzen Sie Ihre Strings mit "L" voran. –