Предположим, у меня есть следующая строка:
s = 'Pingüino: Málaga es una ciudad fantástica y en Logroño me pica el... moño'
Для того, что было, я хочу убрать все тильды и умлауты, чтобы это выглядело так:
s = 'Pinguino: Malaga es una ciudad fantastica y en Logroño me pica el... moño'
# ^ ^ ^
Я обнаружил библиотеку unidecode
, которая делает именно это:
>>> unidecode.unidecode(s)
'Pinguino: Malaga es una ciudad fantastica y en Logrono me pica el... mono'
Но, к сожалению, он также заменяет ñ на n ( Logroño → Logrono , moño → mono ).
Есть ли другая библиотека, которая позволяет эту замену, изменяя только акценты и умлауты? В противном случае я понимаю, что мне нужно сделать регулярное выражение, которое выполняет эту модификацию.
Техника, как правило, та же самая: взять декомпозированную форму нормализации в Unicode, удалить то, что вам не нужно, и вернуться к составной форме.
Разложившаяся форма ?? В Unicode символ (на самом деле «графема») разбивается на эквивалентность его базового символа, за которым следуют его метки. Например:
И разложенная (D), и составная (C) формы эквивалентны ( каноническая эквивалентность Unicode ). Таким образом, их байты разные, но они печатают одинаково
(они по-прежнему являются одной и той же графемой, и существуют алгоритмы для сравнения между формами).
В форме NFD диакритические знаки представляют собой кодовые точки , отделенные от их основного символа (первой кодовой точки )... Это ключ к возможности удалить то, что вам не нужно! И после исключения его можно было бы напечатать в такой форме (D), но удобно вернуться к составной форме, чтобы избежать проблем.
Что удалить? Все варианты действительны. Если они понятны , пройдитесь по выбору в зависимости от того, что вам подходит и что вы предпочитаете применить в вашем случае.
ChemaCortes в своем ответе решил удалить все символы, отличные от ascii (поэтому он временно заменяет их
ñ
другой строкой ascii, которая не удаляется).FJSevilla в ответ нацелила винтовки прямо на знаки ударения (
´
) и умлауты (¨
).Когда основы были покрыты, мне оставалось выбрать самый фундаменталистский вариант: уничтожить все диакритические знаки.
Удалите все диакритические знаки, кроме
ñ
Все диакритические знаки находятся в блоке в диапазоне
U+0300
-U+036F
( Объединение диакритических знаков ). И мы собираемся сделать исключение дляU+0303
тильды (~
), ноn
(заменяя другие какã
)ñ͚͡
)с регулярным выражением, где первая группа является базовым символом, а диакритические знаки находятся вне группы:
([^n\u0300-\u036f])[\u0300-\u036f]+
символ, который не являетсяn
ни а, ни диакритическим знаком, за которым следуют диакритические знаки, или(n(?!\u0303(?![\u0300-\u036f])))[\u0300-\u036f]+
тотn
, за которым не следует~
(если только за последним не следует другой диакритический знак), то он соответствует всем диакритическим знакам, которые следуют за ним.При замене на
\1
остается буква без диакритического знака.Код
https://ideone.com/YcXaQD
Другая возможная идея, извлекаемая также из стандартной библиотеки
unicodedata
, состоит в том, чтобы получить декомпозированную нормализованную форму строки Unicode. Это позволяет"á"
перейти отu"\u00E1"
к,u"\u0061\u0301"
например.Затем просто используйте
str.translate
, чтобы удалить нужные нам кодовые точки Юникода, в данном случаеU+0308
( объединение диэрезиса ) иU+0301
( объединение острого ударения ):В Python 3 вы можете просто сделать это:
К сожалению, не в Python 2 или более ранних версиях вам также придется импортировать модуль String , чтобы иметь возможность использовать string.maketrans() , и при его применении он сообщит вам, что строки a и b имеют разную длину, в факт len(a) = 12 , а len(b) = 6
Для «очистки» строки не требуется ничего, кроме стандартной библиотеки :
Чтобы буквы e не потерялись, их просто заменить символом, который, как вы знаете, не будет использоваться: