Какой компонент обрабатывает комбинированный диарез в строке?
Я работаю со списком имен файлов в Java.
Я заметил, что некоторые отдельные символы в именах файлов, такие как a, ö и ü, на самом деле состоят из последовательности, которую вы можете описать как два отдельных символа ASCII:
ö
представлен o
, ¨
Я вижу это осмотром codePointAt()
, Немецкое имя "Rölli" на самом деле "Ro¨lli":
...
20: R, 82
21: o, 111
22: ̈, 776
23: l, 108
24: l, 108
25: i, 105
...
Характер ¨
в журнале выше имеет значение 776, которое представляет собой "комбинированный диарез". Это так называемая комбинационная метка, которая относится к графемам или, точнее, к диакритическим знакам объединения. Так что все это имеет смысл, но я не понимаю, какой программный компонент объединяет два символа в один умляут, и где указано это поведение.
- Это не имеет ничего общего с тем фактом, что мощные таблицы кодов символов используют несколько байтов в качестве внутреннего представления. Несколько байтов не совпадают с двумя объединяющими символами.
- Любой простой
print()
из строки показывает мне комбинированный символ, так что это не какой-то слой пользовательского интерфейса выше. - Я помню, что наблюдал это также с PHP. Я думаю, что любой современный язык может справиться с этим.
Какой компонент заставляет комбинированные символы отображаться как отдельные комбинированные символы? Насколько все это надежно?
Имеет ли Java метод нормализации, который делает отдельные кодовые точки из комбинированных кодовых точек, как здесь? Была бы помощь для использования Regex...
Большое спасибо за любой намек.
1 ответ
Ответ 1: Спецификация и ответственность
Поведение, которое вы описываете, определено в Стандартном приложении Unicode #15, Формы нормализации Unicode. Речь идет об эквивалентности комбинированных символов и отдельных кодовых точек и о разложении кодовых точек. Многие языки, кроме немецкого, сильно зависят от написания графем.
Java внутренне представляет строки как UTF-16. Так что все это делает с его String
класс поставляет кодовые цепочки UTF-16 другим компонентам. Это зависит от окружающего программного обеспечения (например, любого вида компонентов текстового представления), чтобы правильно комбинировать цепочки. Вы чувствуете это в моменты, когда, например, регулярное выражение нарушает вашу совокупность ö
кроме того, все же это показано правильно в некотором представлении.
Кстати, если вы проводите некоторые эксперименты с комбинированным диарезом, имейте в виду, что существует также "нефункциональный" код 168, который представляет собой простой символ ASCII, называемый "интервальный диарез". Код 168 не заставляет какое-либо программное обеспечение объединять две кодовые точки в одну. Для этого вам нужен Unicode 776.
Ответ 2: метод нормализации Javas
По сути, вы всегда должны учитывать комбинированные символы - за исключением того, что вы уверены, что ваш источник данных не может их доставить. Это хорошая идея, чтобы очистить ваши строки в первую очередь.
Ищите методы нормализации Юникода на вашем языке, так как они освобождают вас от возни с одиночкой replace()
заявления и они содержат большой опыт.
Java имеет Normalizer
объект, который имеет дело с различными представлениями комбинированных символов:
https://docs.oracle.com/javase/7/docs/api/java/text/Normalizer.html
и учебник для него: https://docs.oracle.com/javase/tutorial/i18n/text/normalizerapi.html
Итак, после вызова этой строки кода:
String normalized = Normalizer.normalize(someFileName, Normalizer.Form.NFC);
Печать журнала из приведенного выше вопроса выглядит следующим образом:
...
19: , 32
20: R, 82
21: ö, 246 <<< here were two combined chars before normalize()
22: l, 108
23: l, 108
24: i, 105
...