Jaki jest najlepszy sposób na usunięcie akcentów (normalizację) w łańcuchu Unicode Pythona?
Mam ciąg Unicode w Pythonie i chciałbym usunąć wszystkie akcenty (znaki diakrytyczne).
Znalazłem w sieci elegancki sposób na to (w Javie):
- Konwertuj łańcuch Unicode na jego długo znormalizowana forma (z oddzielnym znakiem dla liter i znaków diakrytycznych)
- usuń wszystkie znaki, których typem Unicode jest "diacritic".
Czy muszę instalować bibliotekę taką jak pyICU czy jest to możliwe tylko za pomocą Python standard library? A co z Pythonem 3?
Ważna uwaga: chciałbym uniknąć kodu z wyraźnym mapowaniem ze znaków akcentowanych do ich nieakcentowanego odpowiednika.
9 answers
Unidecode jest poprawną odpowiedzią na to pytanie. Transliteracja dowolnego ciągu unicode na najbliższą możliwą reprezentację w tekście ascii.
Przykład:
accented_string = u'Málaga'
# accented_string is of type 'unicode'
import unidecode
unaccented_string = unidecode.unidecode(accented_string)
# unaccented_string contains 'Malaga'and is of type 'str'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-21 17:23:40
A może tak:
import unicodedata
def strip_accents(s):
return ''.join(c for c in unicodedata.normalize('NFD', s)
if unicodedata.category(c) != 'Mn')
To działa również na litery greckie:
>>> strip_accents(u"A \u00c0 \u0394 \u038E")
u'A A \u0394 \u03a5'
>>>
Kategoria znaków "Mn" oznacza Nonspacing_Mark
, która jest podobna do unicodedata.łączenie w odpowiedzi Miniquarka (nie myślałem o unicodedata.łączenie, ale to chyba lepsze rozwiązanie, bo jest bardziej wyraziste).
I należy pamiętać, że te manipulacje mogą znacząco zmienić znaczenie tekstu. Akcenty, Umlauty itp. nie są "dekoracją".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-28 13:39:51
Właśnie znalazłem taką odpowiedź w sieci:
import unicodedata
def remove_accents(input_str):
nfkd_form = unicodedata.normalize('NFKD', input_str)
only_ascii = nfkd_form.encode('ASCII', 'ignore')
return only_ascii
To działa dobrze (dla francuskiego, na przykład), ale myślę, że drugi krok (usunięcie akcentów) może być obsługiwany lepiej niż opuszczenie znaków nie-ASCII, ponieważ to nie będzie w niektórych językach(Grecki, na przykład). Najlepszym rozwiązaniem byłoby prawdopodobnie jawne usunięcie znaków unicode, które są oznaczone jako diakrytyczne.
Edit : this does the trick:
import unicodedata
def remove_accents(input_str):
nfkd_form = unicodedata.normalize('NFKD', input_str)
return u"".join([c for c in nfkd_form if not unicodedata.combining(c)])
unicodedata.combining(c)
zwróci true jeśli znak c
może być łączony z poprzedzającym znakiem, to jest głównie, jeśli jest to znak diakrytyczny.
Edytuj 2: remove_accents
oczekuje ciągu unicode, a nie ciągu bajtów. Jeśli masz łańcuch bajtowy, musisz go zdekodować do ciągu unicode, takiego jak:
encoding = "utf-8" # or iso-8859-15, or cp1252, or whatever encoding you use
byte_string = b"café" # or simply "café" before python 3.
unicode_string = byte_string.decode(encoding)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-14 17:01:00
Właściwie pracuję na Pythonie 2.6, 2.7 i 3.4 zgodnym z projektem i muszę tworzyć ID z wolnych wpisów użytkowników.
Dzięki tobie stworzyłem tę funkcję, która czyni cuda.import re
import unicodedata
def strip_accents(text):
"""
Strip accents from input String.
:param text: The input string.
:type text: String.
:returns: The processed String.
:rtype: String.
"""
try:
text = unicode(text, 'utf-8')
except (TypeError, NameError): # unicode is a default on python 3
pass
text = unicodedata.normalize('NFD', text)
text = text.encode('ascii', 'ignore')
text = text.decode("utf-8")
return str(text)
def text_to_id(text):
"""
Convert input text to id.
:param text: The input string.
:type text: String.
:returns: The processed String.
:rtype: String.
"""
text = strip_accents(text.lower())
text = re.sub('[ ]+', '_', text)
text = re.sub('[^0-9a-zA-Z_-]', '', text)
return text
Wynik:
text_to_id("Montréal, über, 12.89, Mère, Françoise, noël, 889")
>>> 'montreal_uber_1289_mere_francoise_noel_889'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-23 22:12:14
To obsługuje nie tylko akcenty, ale także "pociągnięcia" (jak w ø itp.):
import unicodedata as ud
def rmdiacritics(char):
'''
Return the base character of char, by "removing" any
diacritics like accents or curls and strokes and the like.
'''
desc = ud.name(char)
cutoff = desc.find(' WITH ')
if cutoff != -1:
desc = desc[:cutoff]
try:
char = ud.lookup(desc)
except KeyError:
pass # removing "WITH ..." produced an invalid name
return char
To najbardziej elegancki sposób, jaki mogę wymyślić (i został wspomniany przez alexis w komentarzu na tej stronie), chociaż nie sądzę, że jest bardzo elegancki. W rzeczywistości, to jest bardziej hack, jak wskazano w komentarzach, ponieważ nazwy Unicode są-tak naprawdę tylko nazwy, nie dają gwarancji, aby być spójne lub cokolwiek.
Są jeszcze specjalne litery, które nie są obsługiwane przez to, takie jak odwrócone i odwrócone litery, ponieważ ich nazwa unicode nie zawiera 'WITH'. To zależy od tego, co chcesz robić. Czasami potrzebowałem usuwania akcentu, aby osiągnąć kolejność sortowania słownika.
EDIT NOTE:
Włączone sugestie z komentarzy(obsługa błędów wyszukiwania, Kod Pythona-3).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-08 18:19:51
W odpowiedzi na odpowiedź @ MiniQuark:
Próbowałem przeczytać w pliku csv, który był półfrancuski (zawierający akcenty), a także kilka ciągów, które ostatecznie staną się liczbami całkowitymi i pływakami.
Jako test stworzyłem plik test.txt
, który wyglądał tak:
Montréal, über, 12.89, Mère, Françoise, noël, 889
Musiałem dodać linie 2
i 3
, Aby to zadziałało( co znalazłem w bilecie Pythona), a także włączyć @ Jabba ' s komentarz:
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
import csv
import unicodedata
def remove_accents(input_str):
nkfd_form = unicodedata.normalize('NFKD', unicode(input_str))
return u"".join([c for c in nkfd_form if not unicodedata.combining(c)])
with open('test.txt') as f:
read = csv.reader(f)
for row in read:
for element in row:
print remove_accents(element)
Wynik:
Montreal
uber
12.89
Mere
Francoise
noel
889
(Uwaga: jestem na Mac OS X 10.8.4 i używam Pythona 2.7.3)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-12 18:10:05
Gensim.utils.deaccent (text) from Gensim-topic modelling for humans :
'Sef chomutovskych komunistu dostal postou bily prasek'
Innym rozwiązaniem jest unidecode .
Należy zauważyć, że sugerowane rozwiązanie z unicodedata zazwyczaj usuwa akcenty tylko w niektórych znakach (np. zamienia 'ł'
na ''
, a nie na 'l'
).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-16 04:23:00
Niektóre języki mają znaki diakrytyczne łączące jako litery językowe i znaki diakrytyczne akcentu, aby określić akcent.
Myślę, że bezpieczniej jest wyraźnie określić, jakie znaki diakrytyczne chcesz usunąć:
def strip_accents(string, accents=('COMBINING ACUTE ACCENT', 'COMBINING GRAVE ACCENT', 'COMBINING TILDE')):
accents = set(map(unicodedata.lookup, accents))
chars = [c for c in unicodedata.normalize('NFD', string) if c not in accents]
return unicodedata.normalize('NFC', ''.join(chars))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-24 11:34:02
import unicodedata
from random import choice
import perfplot
import regex
import text_unidecode
def remove_accent_chars_regex(x: str):
return regex.sub(r'\p{Mn}', '', unicodedata.normalize('NFKD', x))
def remove_accent_chars_join(x: str):
# answer by MiniQuark
# https://stackoverflow.com/a/517974/7966259
return u"".join([c for c in unicodedata.normalize('NFKD', x) if not unicodedata.combining(c)])
perfplot.show(
setup=lambda n: ''.join([choice('Málaga François Phút Hơn 中文') for i in range(n)]),
kernels=[
remove_accent_chars_regex,
remove_accent_chars_join,
text_unidecode.unidecode,
],
labels=['regex', 'join', 'unidecode'],
n_range=[2 ** k for k in range(22)],
equality_check=None, relative_to=0, xlabel='str len'
)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-02-03 02:59:45