Qual è il modo migliore per rimuovere accenti in una stringa unicode Python?
-
21-08-2019 - |
Domanda
Ho una stringa Unicode in Python, e vorrei togliere tutti gli accenti (segni diacritici).
Ho trovato sul Web un modo elegante per fare questo in Java:
- convertire la stringa Unicode per la sua lunga forma normalizzata (con un carattere separato per le lettere e i segni diacritici)
- rimuovere tutti i caratteri di cui Unicode tipo di "dieresi".
Devo installare una libreria come pyICU o questo è possibile solo con il python standard library?E che dire di python 3?
Nota importante:Vorrei evitare di codice con un mapping esplicito da caratteri accentati non accentati controparte.
Soluzione
Unidecode è la risposta giusta per questo. Si traslittera qualsiasi stringa Unicode nella rappresentazione più vicino possibile in testo ASCII.
Esempio:
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'
Altri suggerimenti
Che ne dite di questo:
import unicodedata
def strip_accents(s):
return ''.join(c for c in unicodedata.normalize('NFD', s)
if unicodedata.category(c) != 'Mn')
Questo funziona su lettere greche, troppo:
>>> strip_accents(u"A \u00c0 \u0394 \u038E")
u'A A \u0394 \u03a5'
>>>
Il categoria carattere "Mn" sta per Nonspacing_Mark
, che è simile a unicodedata.combining in risposta di MiniQuark (non ho pensato di unicodedata.combining, ma è probabilmente la soluzione migliore, perché è più esplicito).
E tenere a mente, queste manipolazioni possono alterare in modo significativo il significato del testo. Accenti, dieresi, ecc non sono "decorazione".
Ho appena trovato questa risposta sul Web:
import unicodedata
def remove_accents(input_str):
nfkd_form = unicodedata.normalize('NFKD', input_str)
only_ascii = nfkd_form.encode('ASCII', 'ignore')
return only_ascii
Funziona bene (per il francese, per esempio), ma penso che il secondo passo (rimuovendo gli accenti) potrebbe essere gestita meglio che cadere i caratteri non-ASCII, perché questa avrà esito negativo per alcune lingue (greco, per esempio) . La soluzione migliore sarebbe probabilmente per rimuovere in modo esplicito i caratteri Unicode che sono contrassegnati come segni diacritici.
Modifica : questo fa il trucco:
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)
restituirà true se il carattere c
può essere combinato con il carattere precedente, cioè soprattutto se si tratta di un diacritico.
Modifica 2 : remove_accents
si aspetta un unicode stringa, non una stringa di byte. Se si dispone di una stringa di byte, quindi è necessario decodificarlo in una stringa unicode in questo modo:
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)
In realtà ho lavorare sul progetto di pitone compatibile 2.6, 2.7 e 3.4 e devo creare ID di voci utente libero.
Grazie a te, ho creato questa funzione che fa miracoli.
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
Risultati:
text_to_id("Montréal, über, 12.89, Mère, Françoise, noël, 889")
>>> 'montreal_uber_1289_mere_francoise_noel_889'
Questo non solo utilizza gli accenti, ma anche "colpi" (come in ø ecc.):
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(unicode(char))
cutoff = desc.find(' WITH ')
if cutoff != -1:
desc = desc[:cutoff]
return ud.lookup(desc)
Questo è il modo più elegante che posso pensare (ed è stata menzionata da alexis in un commento in questa pagina), anche se non credo che è davvero molto elegante.
Ci sono ancora lettere speciali che non sono gestiti da questa, come le lettere tornite e dall'alto in basso, dal momento che il loro nome unicode non contiene 'CON'. Dipende da cosa si vuole fare in ogni caso. A volte mi serviva accento nudo per il raggiungimento di un sistema di classificazione dizionario.
In risposta a @ risposta di MiniQuark:
stavo cercando di leggere in un file CSV che era (gli accenti che contengono) metà francese e anche alcune stringhe che sarebbe poi diventato interi e carri allegorici.
Come prova, ho creato un file test.txt
che si presentava così:
Montréal, über, 12.89, Mère, Françoise, Noël, 889
ho dovuto includere linee 2
e 3
per farlo funzionare (che ho trovato in un biglietto di pitone), così come incorporare @ commento di Jabba:
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)
Il risultato:
Montreal
uber
12.89
Mere
Francoise
noel
889
(Nota: Sono su Mac OS X 10.8.4 e l'utilizzo di Python 2.7.3)
gensim.utils.deaccent(testo) da Gensim - argomento di modellazione per gli esseri umani:
deaccent("Šéf chomutovských komunistů dostal poštou bílý prášek")
'Sef chomutovskych komunistu dostal postou bily prasek'
Un'altra soluzione è unidecode.
Non che la soluzione suggerita con unicodedata in genere toglie accenti solo in alcuni caratteri (ad es.si trasforma 'ł'
in ''
, piuttosto che in 'l'
).
Alcune lingue hanno combinando segni diacritici come lettere linguaggio e segni diacritici accento per specificare accento.
Credo che sia più sicuro di specificare in modo esplicito quello diactrics si vuole mettere a nudo:
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))