J'ai une chaîne Unicode en Python et je voudrais supprimer tous les accents (diacritiques).
J'ai trouvé sur le Web une manière élégante de le faire en Java:
Dois-je installer une bibliothèque telle que pyICU ou est-ce possible uniquement avec la bibliothèque standard python? Et que dire de python 3?
Note importante: je voudrais éviter le code avec un mappage explicite des caractères accentués à leurs équivalents non accentués.
nidecode est la bonne réponse pour cela. Il translittère n'importe quelle chaîne unicode dans la représentation la plus proche possible en texte ASCII.
Exemple:
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'
Que dis-tu de ça:
import unicodedata
def strip_accents(s):
return ''.join(c for c in unicodedata.normalize('NFD', s)
if unicodedata.category(c) != 'Mn')
Cela fonctionne aussi avec les lettres grecques:
>>> strip_accents(u"A \u00c0 \u0394 \u038E")
u'A A \u0394 \u03a5'
>>>
Le catégorie de caractères "Mn" signifie Nonspacing_Mark
, ce qui est similaire à unicodedata.combining dans la réponse de MiniQuark (je n'ai pas pensé à unicodedata.combining, mais c'est probablement la meilleure solution, parce que c'est plus explicite).
Et gardez à l'esprit, ces manipulations peuvent modifier de manière significative le sens du texte. Les accents, les trémas, etc. ne constituent pas une "décoration".
Je viens de trouver cette réponse sur le 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
Cela fonctionne bien (pour le français, par exemple), mais je pense que la deuxième étape (supprimer les accents) pourrait être mieux gérée que de supprimer les caractères non-ASCII, car cela échouera pour certaines langues (le grec, par exemple). La meilleure solution serait probablement de supprimer explicitement les caractères Unicode étiquetés comme étant des signes diacritiques.
Edit: cela fait l'affaire:
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)
retournera true si le caractère c
peut être combiné avec le caractère précédent, c'est-à-dire surtout s'il s'agit d'un diacritique.
Edit 2: remove_accents
attend une chaîne unicode et non une chaîne d'octets. Si vous avez une chaîne d'octets, vous devez alors la décoder en chaîne unicode comme ceci:
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)
En fait, je travaille sur des projets compatibles python 2.6, 2.7 et 3.4 et je dois créer des identifiants à partir d'entrées d'utilisateur gratuites.
Grâce à vous, j'ai créé cette fonction qui fonctionne à merveille.
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
résultat:
text_to_id("Montréal, über, 12.89, Mère, Françoise, noël, 889")
>>> 'montreal_uber_1289_mere_francoise_noel_889'
Cela gère non seulement les accents, mais aussi les "traits" (comme dans ø 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)
C’est la manière la plus élégante à laquelle je puisse penser (et cela a été mentionné par alexis dans un commentaire sur cette page), bien que je ne pense pas que ce soit vraiment très élégant.
Il y a encore des lettres spéciales qui ne sont pas gérées par cela, telles que les lettres en caractères inversés et inversés, car leur nom unicode ne contient pas 'WITH'. Cela dépend de ce que vous voulez faire quand même. J'avais parfois besoin d'éliminer les accents pour obtenir l'ordre de tri du dictionnaire.
En réponse à la réponse de @ MiniQuark:
J'essayais de lire un fichier csv semi-français (contenant des accents) et quelques chaînes qui deviendraient des entiers et des flottants. En guise de test, j'ai créé un fichier test.txt
qui ressemblait à ceci:
Montréal, über, 12.89, Mère, Françoise, noël, 889
J'ai dû inclure les lignes 2
et 3
pour que cela fonctionne (que j'ai trouvé dans un ticket python, _), ainsi que le commentaire 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)
Le résultat:
Montreal
uber
12.89
Mere
Francoise
noel
889
(Remarque: je suis sous Mac OS X 10.8.4 et utilise Python 2.7.3)
gensim.utils.deaccent (text) de Gensim - Modélisation de sujets pour l'homme :
deaccent("Šéf chomutovských komunistů dostal poštou bílý prášek") 'Sef chomutovskych komunistu dostal postou bily prasek'
Une autre solution est nidecode .
Non pas que la solution suggérée avec unicodedata supprime généralement les accents dans certains caractères (par exemple, elle transforme 'ł'
en ''
plutôt que dans 'l'
).
Certaines langues combinent des signes diacritiques en tant que lettres de langue et des signes diacritiques à accent pour spécifier un accent.
Je pense qu'il est plus sûr de spécifier explicitement les diactriques que vous voulez effacer:
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))