web-dev-qa-db-fra.com

Quel est le meilleur moyen de supprimer les accents dans une chaîne unicode Python?

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:

  1. convertir la chaîne Unicode en sa forme normalisée longue (avec un caractère séparé pour les lettres et les signes diacritiques)
  2. supprime tous les caractères dont le type Unicode est "diacritique".

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.

437
MiniQuark

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'
371
Christian Oudard

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".

256
oefe

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)
132
MiniQuark

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'
34
hexaJer

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.

18
lenz

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)

14
aseagram

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').

12
Piotr Migdal

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))
3
sirex