Regex: что такое InCombiningDiacriticalMarks?

86

Очень хорошо известен следующий код для преобразования символов с диакритическими знаками в обычный текст:

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

Я заменил свой метод "ручной работы" на этот, но мне нужно понимать "регулярное выражение" в replaceAll

1) Что такое «InCombiningDiacriticalMarks»?
2) Где это документация? (и подобные?)

Спасибо.

марколопы
источник
См. Также stackoverflow.com/a/29111105/32453, по- видимому, в Юникоде больше «комбинирующих знаков», чем просто диакритических, как примечание.
rogerdpack

Ответы:

74

\p{InCombiningDiacriticalMarks}является свойством блока Unicode. В JDK7 вы сможете написать его, используя двухчастную нотацию \p{Block=CombiningDiacriticalMarks}, которая может быть более понятной для читателя. Это задокументировано здесь, в UAX № 44: «База данных символов Unicode» .

Это означает, что кодовая точка попадает в конкретный диапазон, блок, который был выделен для использования для вещей под этим именем. Это плохой подход, потому что нет гарантии, что кодовая точка в этом диапазоне является или не является какой-либо конкретной вещью, или что кодовые точки за пределами этого блока не имеют по существу одного и того же символа.

Например, в \p{Latin_1_Supplement}блоке есть латинские буквы , такие как é, U + 00E9. Однако там есть вещи, которые не являются латинскими буквами. И, конечно же, повсюду встречаются латинские буквы.

Блоки - это почти никогда не то, что вам нужно.

В этом случае я подозреваю, что вы можете захотеть использовать свойство \p{Mn}, также известное как \p{Nonspacing_Mark}. Все кодовые точки в блоке Combining_Diacriticals относятся к этому типу. Также есть (начиная с Unicode 6.0.0) 1087 Nonspacing_Marks, которых нет в этом блоке.

Это почти то же самое , как проверка \p{Bidi_Class=Nonspacing_Mark}, но не совсем, потому что группа также включает в себя ограждающие знаки, \p{Me}. Если вам нужно и то, и другое, вы можете сказать, используете [\p{Mn}\p{Me}]ли вы движок регулярных выражений Java по умолчанию, поскольку он дает доступ только к свойству General_Category.

Вам придется использовать JNI для доступа к библиотеке регулярных выражений ICU C ++, как это делает Google, чтобы получить доступ к чему-то вроде \p{BC=NSM}, потому что сейчас только ICU и Perl предоставляют доступ ко всем свойствам Unicode. Обычная библиотека регулярных выражений Java поддерживает только несколько стандартных свойств Unicode. Однако в JDK7 будет поддержка свойства Unicode Script, которое почти бесконечно предпочтительнее свойства Block. Таким образом, в JDK7 вы можете написать \p{Script=Latin}или \p{SC=Latin}, или сокращенно \p{Latin}, получить любой символ из латинского алфавита. Это приводит к очень часто необходимым [\p{Latin}\p{Common}\p{Inherited}].

Имейте в виду, что это не приведет к удалению того, что вы могли бы подумать как «акцентные» знаки со всех символов! Для многих это не годится. Например, вы не можете преобразовать Đ в D или ø в o таким образом. Для этого вам нужно уменьшить количество кодовых точек до тех, которые соответствуют той же основной силе сопоставления в таблице сопоставления Unicode.

Другое место, где что- \p{Mn}то не работает, - это, конечно, пометки вроде \p{Me}, очевидно, но также есть \p{Diacritic}символы, которые не являются знаками. К сожалению, для этого вам нужна полная поддержка свойств, что означает JNI для ICU или Perl. Боюсь, у Java много проблем с поддержкой Unicode.

Ой, подождите, я вижу, вы португальцы. У вас не должно возникнуть никаких проблем, если вы имеете дело только с португальским текстом.

Однако я уверен, что вы действительно не хотите убирать акценты, а скорее хотите иметь возможность сочетать вещи «без учета акцента», верно? Если да, то вы можете сделать это с помощью класса сопоставителя ICU4J (ICU для Java) . Если вы сравните по основной силе, акцентные знаки не будут учитываться. Я делаю это все время, потому что часто обрабатываю испанский текст. У меня есть пример того, как это сделать для испанцев, сидящих где-нибудь здесь, если вам это нужно.

Христос
источник
Итак, я должен предположить, что метод, представленный в Интернете (и даже здесь, в SO), не рекомендуется для слова "DeAccent". Я сделал простой только для португальского языка, но увидел этот странный подход (и, как вы сказали, он работает для моей цели, но мой последний метод работал!). Итак, есть ли более «хорошо реализованный» подход, который охватит большинство сценариев? Пример был бы очень хорош. Спасибо за ваше время.
marcolopes
1
@Marcolopes: я оставляю данные нетронутыми и использую алгоритм сортировки Unicode для сравнений первичной силы. Таким образом, он просто сравнивает буквы, но игнорирует регистр и диакритические знаки. Это также позволяет вещам, которые должны быть одной и той же буквой, быть одной и той же буквой, удаление акцентов является лишь слабым и неудовлетворительным приближением. Кроме того, лучше не выгружать данные, если вы можете работать с ними так, как хотите, но не требуют этого.
tchrist
Довольно хороший ответ. Один вопрос: могу ли я использовать нормализатор в java и использовать InCombiningDiacriticalMarks, но исключить некоторые символы, такие как ü, из преобразования в u?
AlexCon
6
да, я полностью понял все это
Донал
4

Мне потребовалось время, но я выловил их всех:

Вот регулярное выражение, которое должно включать все символы zalgo, включая те, которые были обойдены в «нормальном» диапазоне.

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

Надеюсь, это сэкономит вам время.

Матас Вайткявичюс
источник