Regex: co to jest InCombiningDiacriticalMarks?

86

Poniższy kod jest dobrze znany z konwertowania znaków akcentowanych na zwykły tekst:

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

Zastąpiłem moją metodę „ręcznie tworzoną” tą, ale muszę zrozumieć część „wyrażenia regularnego” metody replaceAll

1) Co to jest „InCombiningDiacriticalMarks”?
2) Gdzie jest to dokumentacja? (i podobne?)

Dzięki.

markolopy
źródło
Zobacz także stackoverflow.com/a/29111105/32453 Najwyraźniej w Unicode jest więcej „znaków łączących” niż tylko znaki diakrytyczne, tak jak notatka.
rogerdpack

Odpowiedzi:

74

\p{InCombiningDiacriticalMarks}jest właściwością bloku Unicode. W JDK7 będziesz mógł to napisać używając dwuczęściowej notacji \p{Block=CombiningDiacriticalMarks}, która może być bardziej czytelna dla czytelnika. Jest to udokumentowane tutaj w UAX # 44: „Baza znaków Unicode” .

Oznacza to, że punkt kodowy należy do określonego zakresu, bloku, który został przydzielony do użycia dla rzeczy o tej nazwie. Jest to złe podejście, ponieważ nie ma gwarancji, że punkt kodowy w tym zakresie jest lub nie jest jakąś szczególną rzeczą, ani że punkty kodowe poza tym blokiem nie mają zasadniczo tego samego charakteru.

Na przykład w \p{Latin_1_Supplement}bloku są litery łacińskie , takie jak é, U + 00E9. Jednak są tam również rzeczy, które nie są literami łacińskimi. Oczywiście wszędzie są też litery łacińskie.

Bloki prawie nigdy nie są tym, czego chcesz.

W tym przypadku podejrzewam, że możesz chcieć skorzystać z nieruchomości \p{Mn}, aka \p{Nonspacing_Mark}. Wszystkie punkty kodowe w bloku Combining_Diacriticals są tego rodzaju. Istnieją również (od Unicode 6.0.0) 1087 Nonspacing_Marks, których nie ma w tym bloku.

To jest prawie taki sam, jak sprawdzanie \p{Bidi_Class=Nonspacing_Mark}, ale nie do końca, ponieważ ta grupa obejmuje również znaki załączając, \p{Me}. Jeśli chcesz mieć oba, możesz powiedzieć [\p{Mn}\p{Me}], że używasz domyślnego silnika wyrażeń regularnych Java, ponieważ zapewnia on dostęp tylko do właściwości General_Category.

Musiałbyś użyć JNI, aby dostać się do biblioteki wyrażeń regularnych ICU C ++ tak, jak robi to Google, aby uzyskać dostęp do czegoś podobnego \p{BC=NSM}, ponieważ obecnie tylko ICU i Perl dają dostęp do wszystkich właściwości Unicode. Normalna biblioteka wyrażeń regularnych Java obsługuje tylko kilka standardowych właściwości Unicode. Jednak w JDK7 będzie wsparcie dla właściwości Unicode Script, która jest prawie nieskończenie lepsza od właściwości Block. W ten sposób możesz w JDK7 napisać \p{Script=Latin}lub \p{SC=Latin}, lub skrótem \p{Latin}, dostać się do dowolnego znaku z alfabetu łacińskiego. Prowadzi to do bardzo powszechnie potrzebnych [\p{Latin}\p{Common}\p{Inherited}].

Pamiętaj, że to nie usunie tego, co możesz pomyśleć jako znaki „akcentu” ze wszystkich znaków! Jest wielu, dla których tego nie zrobi. Na przykład nie można w ten sposób zamienić Đ na D ani ø na o . W tym celu należy zredukować punkty kodowe do tych, które mają tę samą podstawową siłę sortowania w tabeli sortowania Unicode.

Innym miejscem, w którym \p{Mn}coś się nie udaje, jest oczywiście otaczanie znaków, takich jak \p{Me}, oczywiście, ale są też \p{Diacritic}znaki, które nie są znakami. Niestety, potrzebujesz do tego pełnej obsługi nieruchomości, co oznacza JNI dla ICU lub Perla. Obawiam się, że Java ma wiele problemów z obsługą Unicode.

Och, czekaj, widzę, że jesteś Portugalczykiem. Nie powinieneś mieć żadnych problemów, jeśli masz do czynienia tylko z tekstem portugalskim.

Założę się jednak, że tak naprawdę nie chcesz usuwać akcentów, ale raczej chcesz dopasować rzeczy „niewrażliwie na akcent”, prawda? Jeśli tak, możesz to zrobić za pomocą klasy zbierającej ICU4J (ICU for Java) . Jeśli porównasz siłę podstawową, znaki akcentu nie będą się liczyć. Robię to cały czas, ponieważ często przetwarzam tekst w języku hiszpańskim. Mam przykład, jak to zrobić dla hiszpańskiego siedzącego gdzieś tutaj, jeśli tego potrzebujesz.

tchrist
źródło
Muszę więc założyć, że metoda podana w całej sieci (a nawet tutaj w SO) nie jest zalecana dla słowa „DeAccent”. Zrobiłem prostą tylko dla portugalskiego, ale widziałem to dziwne podejście (i jak powiedziałeś, działa w moim celu, ale moja ostatnia metoda tak!). Czy istnieje więc lepsze „dobrze wdrożone” podejście, które obejmie większość scenariuszy? Przykład byłby bardzo fajny. Dziękuję za Twój czas.
marcolopes
1
@Marcolopes: Zostawiłem dane w stanie nienaruszonym i używam algorytmu sortowania Unicode do porównań siły podstawowej. W ten sposób po prostu porównuje litery, ale ignoruje zarówno wielkie litery, jak i znaki akcentu. Pozwala również, aby rzeczy, które powinny być tą samą literą, były tą samą literą, a usunięcie akcentów jest tylko bladym i niezadowalającym przybliżeniem. Poza tym czystsze jest nie niszczenie danych, jeśli możesz pracować z nimi w sposób, który robi to, co chcesz, ale nie wymaga tego.
tchrist
Całkiem dobra odpowiedź. Jedno pytanie, czy mogę używać Normalizera w javie i używać InCombiningDiacriticalMarks, ale wykluczyć niektóre znaki, takie jak ü, z konwersji na u?
AlexCon
6
tak, całkowicie to wszystko zrozumiałem
Dónal
4

Zajęło mi to chwilę, ale wyłowiłem je wszystkie:

Oto wyrażenie regularne, które powinno zawierać wszystkie znaki zalgo, w tym te pomijane w „normalnym” zakresie.

([\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])

Mam nadzieję, że zaoszczędzi ci to trochę czasu.

Matas Vaitkevicius
źródło