web-dev-qa-db-fra.com

remplacer efficacement les mauvais caractères

Je travaille souvent avec du texte utf-8 contenant des caractères comme:

\ xc2\x99

\ xc2\x95

\ xc2\x85

etc

Ces caractères confondent les autres bibliothèques avec lesquelles je travaille et doivent donc être remplacés.

Quelle est la manière efficace de procéder, plutôt que:

text.replace('\xc2\x99', ' ').replace('\xc2\x85, '...')
24
hoju

Il y a toujours des expressions régulières; il suffit de lister tous les caractères incriminés entre crochets comme ceci:

import re
print re.sub(r'[\xc2\x99]'," ","Hello\xc2There\x99")

Cela affiche: "Hello There", avec les caractères indésirables remplacés par des espaces.

Alternativement, si vous avez un caractère de remplacement différent pour chacun:

# remove annoying characters
chars = {
    '\xc2\x82' : ',',        # High code comma
    '\xc2\x84' : ',,',       # High code double comma
    '\xc2\x85' : '...',      # Tripple dot
    '\xc2\x88' : '^',        # High carat
    '\xc2\x91' : '\x27',     # Forward single quote
    '\xc2\x92' : '\x27',     # Reverse single quote
    '\xc2\x93' : '\x22',     # Forward double quote
    '\xc2\x94' : '\x22',     # Reverse double quote
    '\xc2\x95' : ' ',
    '\xc2\x96' : '-',        # High hyphen
    '\xc2\x97' : '--',       # Double hyphen
    '\xc2\x99' : ' ',
    '\xc2\xa0' : ' ',
    '\xc2\xa6' : '|',        # Split vertical bar
    '\xc2\xab' : '<<',       # Double less than
    '\xc2\xbb' : '>>',       # Double greater than
    '\xc2\xbc' : '1/4',      # one quarter
    '\xc2\xbd' : '1/2',      # one half
    '\xc2\xbe' : '3/4',      # three quarters
    '\xca\xbf' : '\x27',     # c-single quote
    '\xcc\xa8' : '',         # modifier - under curve
    '\xcc\xb1' : ''          # modifier - under line
}
def replace_chars(match):
    char = match.group(0)
    return chars[char]
return re.sub('(' + '|'.join(chars.keys()) + ')', replace_chars, text)
35
Nate

Je pense qu'il y a un problème sous-jacent ici, et ce pourrait être une bonne idée d'enquêter et peut-être de le résoudre, plutôt que d'essayer simplement de dissimuler les symptômes.

\xc2\x95 est le codage UTF-8 du caractère U + 0095, qui est un caractère de contrôle C1 (MESSAGE WAITING). Il n'est pas surprenant que votre bibliothèque ne puisse pas le gérer. Mais la question est, comment a-t-elle pénétré vos données?

Eh bien, une possibilité très probable est qu'il a commencé comme le caractère 0x95 (BULLET) dans le codage Windows-1252 , a été incorrectement décodé en U + 0095 au lieu du bon U + 2022, puis codé en UTF-8. (Le terme japonais mojibake décrit ce genre d'erreur.)

Si cela est correct, vous pouvez récupérer les caractères originaux en les replaçant dans Windows-1252 puis en les décodant correctement en Unicode cette fois. (Dans ces exemples, j'utilise Python 3.3; ces opérations sont un peu différentes dans Python 2.)

>>> b'\x95'.decode('windows-1252')
'\u2022'
>>> import unicodedata
>>> unicodedata.name(_)
'BULLET'

Si vous souhaitez effectuer cette correction pour tous les caractères de la plage 0x80–0x99 qui sont des caractères Windows-1252 valides, vous pouvez utiliser cette approche:

def restore_windows_1252_characters(s):
    """Replace C1 control characters in the Unicode string s by the
    characters at the corresponding code points in Windows-1252,
    where possible.

    """
    import re
    def to_windows_1252(match):
        try:
            return bytes([ord(match.group(0))]).decode('windows-1252')
        except UnicodeDecodeError:
            # No character at the corresponding code point: remove it.
            return ''
    return re.sub(r'[\u0080-\u0099]', to_windows_1252, s)

Par exemple:

>>> restore_windows_1252_characters('\x95\x99\x85')
'•™…'
23
Gareth Rees

Si vous souhaitez supprimer tous les caractères non ASCII d'une chaîne, vous pouvez utiliser

text.encode("ascii", "ignore")
11
Tim Pietzcker
import unicodedata

# Convert to unicode
text_to_uncicode = unicode(text, "utf-8")           

# Convert back to ascii
text_fixed = unicodedata.normalize('NFKD',text_to_unicode).encode('ascii','ignore')         
2
Ady

Ce ne sont pas des "caractères Unicode" - cela ressemble plus à ceci une chaîne encodée UTF-8. (Bien que votre préfixe soit\xC3, pas\xC2 pour la plupart des caractères). Vous ne devez pas simplement les jeter dans 95% des cas, sauf si vous communiquez avec un backend COBOL. Le monde n'est pas limité à 26 caractères, vous savez.

Il y a une lecture concise pour expliquer les différences entre les chaînes Unicode (ce qui est utilisé comme objet Unicode dans python 2 et comme chaînes dans Python 3 ici: - http://www.joelonsoftware.com/articles/Unicode.html - s'il vous plaît, pour votre plaisir, lisez cela. Même si vous ne prévoyez jamais d'avoir quelque chose qui n'est pas anglais dans toutes vos applications, vous tomberez toujours sur des symboles comme € ou º qui ne rentrent pas dans ASCII 7 bits. Cet article vous aidera.

Cela dit, les bibliothèques que vous utilisez acceptent peut-être les objets Unicode python, et vous pouvez transformer vos chaînes UTF-8 Python 2 en unidoce en faisant:

var_unicode = var.decode("utf-8")

Si vous avez vraiment besoin d'un ASCII pur à 100%, en remplaçant tous les caractères non ASCII, après décodage de la chaîne en unicode, re -encode-le en ASCII, en lui disant d'ignorer les caractères qui ne rentrent pas dans le jeu de caractères avec:

var_ascii = var_unicode.encode("ascii", "replace")
0
jsbueno

Ces caractères ne sont pas dans la bibliothèque ASCII et c'est la raison pour laquelle vous obtenez les erreurs. Pour éviter ces erreurs, vous pouvez effectuer les opérations suivantes lors de la lecture du fichier.

import codecs   
f = codecs.open('file.txt', 'r',encoding='utf-8')

Pour en savoir plus sur ce type d'erreurs, passez par ce lien .

0
Mokshith Sandeep