Qual é a melhor maneira de remover acentos em uma seqüência de caracteres Unicode Python?
-
21-08-2019 - |
Pergunta
Eu tenho uma seqüência de caracteres Unicode em Python, e eu gostaria de remover todas as Acentos (diacríticos).
I encontrado na Web uma maneira elegante de fazer isso em Java:
- converter a seqüência de caracteres Unicode para sua forma normalizada longa (com um personagem separado para letras e sinais diacríticos)
- remover todos os personagens cujo tipo Unicode é "diacrítico".
Do I necessidade de instalar uma biblioteca, como pyICU ou isso é possível apenas com a biblioteca padrão python? E o que dizer python 3?
Nota importante:. Gostaria de código de evitar com um mapeamento explícito de caracteres acentuados à sua contraparte não-acentuado
Solução
Unidecode é a resposta correta para isso. Ele translitera qualquer seqüência de caracteres Unicode para o mais próximo possível representação em texto ASCII.
Exemplo:
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'
Outras dicas
Como sobre isto:
import unicodedata
def strip_accents(s):
return ''.join(c for c in unicodedata.normalize('NFD', s)
if unicodedata.category(c) != 'Mn')
Isso funciona em letras gregas, também:
>>> strip_accents(u"A \u00c0 \u0394 \u038E")
u'A A \u0394 \u03a5'
>>>
O href="http://www.unicode.org/reports/tr44/#GC_Values_Table" categoria caráter "Mn" significa Nonspacing_Mark
, que é semelhante ao unicodedata .combining na resposta de MiniQuark (eu não penso em unicodedata.combining, mas é provavelmente a melhor solução, porque é mais explícito).
E lembre-se, estas manipulações podem alterar significativamente o significado do texto. Acentos, tremas, etc. não são "decoração".
Eu encontrei esta resposta na 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
Ele funciona muito bem (para o francês, por exemplo), mas acho que a segunda etapa (removendo os acentos) poderia ser tratada melhor do que deixar cair os caracteres não-ASCII, porque isso irá falhar por algumas línguas (grego, por exemplo) . A melhor solução seria provavelmente para remover explicitamente os caracteres Unicode que são marcados como sendo sinais diacríticos.
Editar : isso faz o truque:
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)
retornará true se o c
personagem pode ser combinado com o personagem anterior, que é principalmente se é um sinal diacrítico.
Editar 2 : remove_accents
espera um unicode string, não uma seqüência de byte. Se você tem uma cadeia de bytes, então você deve decodificá-lo em uma string unicode assim:
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)
Na verdade, eu trabalho no projeto python compatível 2.6, 2.7 e 3.4 e eu tenho que criar IDs de entradas de usuários livres.
Graças a você, eu criei esta função que faz maravilhas.
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
resultado:
text_to_id("Montréal, über, 12.89, Mère, Françoise, noël, 889")
>>> 'montreal_uber_1289_mere_francoise_noel_889'
Este alças não só acentos, mas também "traços" (como em ø etc.):
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)
Esta é a maneira mais elegante que eu posso pensar (e tem sido mencionado por alexis em um comentário sobre esta página), embora eu não acho que é muito elegante, de fato.
Ainda há letras especiais que não são tratados por este, como cartas virou e invertidas, já que seu nome unicode não contém 'com'. Depende do que você quer fazer de qualquer maneira. Às vezes eu precisava sotaque descascar para alcançar ordem de classificação dicionário.
Em resposta a resposta da @ MiniQuark:
Eu estava tentando ler em um arquivo CSV que era meio francesa (que contêm acentos) e também algumas cordas que acabaria por se tornar inteiros e carros alegóricos.
Como um teste, eu criei um arquivo test.txt
que ficou assim:
Montréal, über, 12,89, Mère, Françoise, Noel, 889
Eu tive que incluem linhas 2
e 3
para obtê-lo para o trabalho (que eu encontrei em um bilhete de python), bem como incorporar @ comentário de 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)
O resultado:
Montreal
uber
12.89
Mere
Francoise
noel
889
(Nota: Eu estou no Mac OS X 10.8.4 e usando Python 2.7.3)
gensim.utils.deaccent (texto) de Gensim - modelagem tópico para os seres humanos :
deaccent("Šéf chomutovských komunistů dostal poštou bílý prášek")
'Sef chomutovskych komunistu dostal postou bily prasek'
Outra solução é unidecode .
Não que a solução sugerida com unicodedata ??strong> tipicamente remove acentos apenas em alguns caracteres (por exemplo, ele se transforma 'ł'
em ''
, ao invés de em 'l'
).
Algumas línguas têm combinando diacríticos como cartas de línguas e sinais diacríticos acento para especificar sotaque.
Eu acho que é mais seguro para especificar explicitamente o que diactrics você quiser tirar:
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))