Regex: Was ist InCombiningDiacriticalMarks?

85

Der folgende Code ist sehr bekannt dafür, Zeichen mit Akzent in einfachen Text umzuwandeln:

Normalizer.normalize(text, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", "");

Ich habe meine "handgemachte" Methode durch diese ersetzt, aber ich muss den "Regex" -Teil von replaceAll verstehen

1) Was ist "InCombiningDiacriticalMarks"?
2) Wo ist die Dokumentation davon? (und ähnliche?)

Vielen Dank.

marcolopes
quelle
Siehe auch stackoverflow.com/a/29111105/32453 anscheinend gibt es im Unicode mehr "Kombinationsmarken" als nur diakritische, nur als Hinweis.
Rogerdpack

Antworten:

73

\p{InCombiningDiacriticalMarks}ist eine Unicode-Blockeigenschaft. In JDK7 können Sie es mit der zweiteiligen Notation schreiben \p{Block=CombiningDiacriticalMarks}, die für den Leser möglicherweise klarer ist. Es ist hier in UAX # 44 dokumentiert : "The Unicode Character Database" .

Dies bedeutet, dass der Codepunkt in einen bestimmten Bereich fällt, einen Block, der für die Dinge mit diesem Namen verwendet wurde. Dies ist ein schlechter Ansatz, da nicht garantiert werden kann, dass der Codepunkt in diesem Bereich eine bestimmte Sache ist oder nicht, und dass Codepunkte außerhalb dieses Blocks nicht im Wesentlichen dasselbe Zeichen haben.

Zum Beispiel enthält der \p{Latin_1_Supplement}Block lateinische Buchstaben wie é, U + 00E9. Es gibt jedoch auch Dinge, die dort keine lateinischen Buchstaben sind. Und natürlich gibt es überall auch lateinische Buchstaben.

Blöcke sind fast nie das, was Sie wollen.

In diesem Fall vermute ich, dass Sie die Eigenschaft verwenden möchten \p{Mn}, auch bekannt als \p{Nonspacing_Mark}. Alle Codepunkte im Block Combining_Diacriticals sind von dieser Art. Es gibt auch (ab Unicode 6.0.0) 1087 Nonspacing_Marks, die sich nicht in diesem Block befinden.

Das ist fast das Gleiche wie das Überprüfen \p{Bidi_Class=Nonspacing_Mark}, aber nicht ganz, da diese Gruppe auch die umschließenden Markierungen enthält \p{Me}. Wenn Sie beides möchten, können Sie sagen, [\p{Mn}\p{Me}]ob Sie eine Standard-Java-Regex-Engine verwenden, da diese nur Zugriff auf die Eigenschaft General_Category bietet.

Sie müssten JNI verwenden, um auf die ICU C ++ - Regex-Bibliothek zuzugreifen, wie es Google tut, um auf so etwas zuzugreifen \p{BC=NSM}, da derzeit nur ICU und Perl Zugriff auf alle Unicode-Eigenschaften gewähren . Die normale Java-Regex-Bibliothek unterstützt nur einige Standardeigenschaften von Unicode. In JDK7 wird jedoch die Unicode-Skript-Eigenschaft unterstützt, die der Block-Eigenschaft nahezu unendlich vorzuziehen ist. So können Sie in JDK7 schreiben \p{Script=Latin}oder \p{SC=Latin}oder die Abkürzung \p{Latin}, um auf ein beliebiges Zeichen aus der lateinischen Schrift zuzugreifen . Dies führt zu den sehr häufig benötigten [\p{Latin}\p{Common}\p{Inherited}].

Beachten Sie, dass dadurch nicht alle Zeichen entfernt werden, die Sie als „Akzent“ -Marken betrachten könnten! Es gibt viele, für die dies nicht möglich ist. Zum Beispiel können Sie nicht umwandeln Đ bis D oder ø bis o auf diese Weise. Dazu müssen Sie die Codepunkte auf diejenigen reduzieren, die der gleichen primären Kollatierungsstärke in der Unicode-Kollatierungstabelle entsprechen.

Ein anderer Ort, an dem das \p{Mn}Ding versagt, ist natürlich das Einschließen von Markierungen wie \p{Me}natürlich, aber es gibt auch \p{Diacritic}Zeichen, die keine Markierungen sind. Leider benötigen Sie dafür volle Immobilienunterstützung, was bedeutet, dass JNI entweder auf der Intensivstation oder in Perl verfügbar ist. Ich fürchte, Java hat viele Probleme mit der Unicode-Unterstützung.

Oh warte, ich sehe, du bist Portugiese. Sie sollten dann überhaupt keine Probleme haben, wenn Sie sich nur mit portugiesischem Text beschäftigen.

Ich wette, Sie möchten Akzente nicht wirklich entfernen, sondern in der Lage sein, Dinge „akzentunempfindlich“ abzugleichen, oder? Wenn ja, können Sie dies mit der Collator-Klasse ICU4J (ICU für Java) tun . Wenn Sie mit der Primärstärke vergleichen, zählen Akzentzeichen nicht. Ich mache das die ganze Zeit, weil ich oft spanischen Text verarbeite. Ich habe ein Beispiel dafür, wie man das für Spanisch macht, wenn man hier irgendwo herumsitzt, wenn man es braucht.

tchrist
quelle
Ich muss also davon ausgehen, dass die im gesamten Web (und auch hier bei SO) angegebene Methode nicht die empfohlene für "DeAccent" ist. Ich habe eine gerade für Portugiesisch gemacht, aber diesen seltsamen Ansatz gesehen (und wie Sie sagten, funktioniert er für meinen Zweck, aber meine letzte Methode hat es getan!). Gibt es also einen besseren "gut implementierten" Ansatz, der die meisten Szenarien abdeckt? Ein Beispiel wäre sehr schön. Vielen Dank für Ihre Zeit.
marcolopes
1
@Marcolopes: Ich habe die Daten intakt gelassen und den Unicode-Kollatierungsalgorithmus verwendet, um Vergleiche der Primärstärke durchzuführen. Auf diese Weise werden nur Buchstaben verglichen, aber sowohl Groß- als auch Kleinschreibung ignoriert. Es lässt auch Dinge, die der gleiche Buchstabe sein sollten , der gleiche Buchstabe sein , was das Entfernen der Akzente nur eine blasse und unbefriedigende Annäherung an ist. Außerdem ist es sauberer, die Daten nicht zu zappen, wenn Sie so damit arbeiten können, dass Sie das tun, was Sie wollen, dies aber nicht benötigen.
Tchrist
Ziemlich gute Antwort. Eine Frage: Kann ich Normalizer in Java verwenden und InCombiningDiacriticalMarks verwenden, aber einige Zeichen wie ü von der Konvertierung in u ausschließen?
AlexCon
6
Ja, ich habe das alles total verstanden
Dónal
4

Es hat eine Weile gedauert, aber ich habe sie alle gefischt:

Hier ist der reguläre Ausdruck, der alle Zalgo-Zeichen enthalten sollte, einschließlich derjenigen, die im "normalen" Bereich umgangen wurden.

([\u0300\u036F\u1AB0\u1AFF\u1DC0\u1DFF\u20D0\u20FF\uFE20\uFE2F\u0483-\u0486\u05C7\u0610-\u061A\u0656-\u065F\u0670\u06D6-\u06ED\u0711\u0730-\u073F\u0743-\u074A\u0F18-\u0F19\u0F35\u0F37\u0F72-\u0F73\u0F7A-\u0F81\u0F84\u0e00-\u0eff\uFC5E-\uFC62])

Hoffe das spart dir etwas Zeit.

Matas Vaitkevicius
quelle