2 votes

comment lire un fichier qui peut être enregistré soit en ansi soit en unicode en python ?

Je dois écrire un script qui supporte la lecture d'un fichier qui peut être sauvegardé soit en Unicode soit en Ansi (en utilisant le notepad de MS).

Je n'ai aucune indication du format d'encodage dans le fichier, comment puis-je supporter les deux formats d'encodage ? (il s'agit en quelque sorte d'une manière générique de lire les fichiers sans connaître le format de manière avancée).

16voto

John Machin Points 39706

MS Notepad propose à l'utilisateur un choix de 4 encodages, exprimés dans une terminologie maladroite et confuse :

"Unicode" est UTF-16, écrit en little-endian. "Unicode big endian" est UTF-16, écrit big-endian. Dans les deux cas UTF-16, cela signifie que la nomenclature appropriée sera écrite. Utilisez utf-16 pour décoder un tel fichier.

"UTF-8" est UTF-8 ; Notepad écrit explicitement un "UTF-8 BOM". Utilisez utf-8-sig pour décoder un tel fichier.

"ANSI" est un choc. C'est la terminologie MS pour "quel que soit l'encodage par défaut sur cet ordinateur".

Voici une liste des encodages Windows que je connais et les langages/scripts pour lesquels ils sont utilisés :

cp874  Thai
cp932  Japanese 
cp936  Unified Chinese (P.R. China, Singapore)
cp949  Korean 
cp950  Traditional Chinese (Taiwan, Hong Kong, Macao(?))
cp1250 Central and Eastern Europe 
cp1251 Cyrillic ( Belarusian, Bulgarian, Macedonian, Russian, Serbian, Ukrainian)
cp1252 Western European languages
cp1253 Greek 
cp1254 Turkish 
cp1255 Hebrew 
cp1256 Arabic script
cp1257 Baltic languages 
cp1258 Vietnamese
cp???? languages/scripts of India  

Si le fichier a été créé sur l'ordinateur sur lequel il est lu, vous pouvez obtenir l'encodage "ANSI" de la manière suivante locale.getpreferredencoding() . Sinon, si vous savez d'où il provient, vous pouvez spécifier l'encodage à utiliser s'il n'est pas UTF-16. À défaut, devinez.

Faites attention en utilisant codecs.open() pour lire les fichiers sous Windows. La documentation dit : """Note Les fichiers sont toujours ouverts en mode binaire, même si aucun mode binaire n'a été spécifié. Ceci est fait pour éviter la perte de données due aux encodages utilisant des valeurs 8 bits. Cela signifie qu'aucune conversion automatique de ' \n ' est fait sur la lecture et l'écriture.""" Cela signifie que vos lignes se termineront par \r\n et vous aurez besoin de les enlever.

Tout mettre en place :

Un exemple de fichier texte, enregistré avec les 4 choix d'encodage, ressemble à ceci dans Notepad :

The quick brown fox jumped over the lazy dogs.
àáâãäå

Voici un code de démonstration :

import locale

def guess_notepad_encoding(filepath, default_ansi_encoding=None):
    with open(filepath, 'rb') as f:
        data = f.read(3)
    if data[:2] in ('\xff\xfe', '\xfe\xff'):
        return 'utf-16'
    if data == u''.encode('utf-8-sig'):
        return 'utf-8-sig'
    # presumably "ANSI"
    return default_ansi_encoding or locale.getpreferredencoding()

if __name__ == "__main__":
    import sys, glob, codecs
    defenc = sys.argv[1]
    for fpath in glob.glob(sys.argv[2]):
        print
        print (fpath, defenc)
        with open(fpath, 'rb') as f:
            print "raw:", repr(f.read())
        enc = guess_notepad_encoding(fpath, defenc)
        print "guessed encoding:", enc
        with codecs.open(fpath, 'r', enc) as f:
            for lino, line in enumerate(f, 1):
                print lino, repr(line)
                print lino, repr(line.rstrip('\r\n'))

et voici le résultat lorsqu'il est exécuté dans une fenêtre "Invite de commande" de Windows en utilisant la commande \python27\python read_notepad.py "" t1-*.txt

('t1-ansi.txt', '')
raw: 'The quick brown fox jumped over the lazy dogs.\r\n\xe0\xe1\xe2\xe3\xe4\xe5
\r\n'
guessed encoding: cp1252
1 u'The quick brown fox jumped over the lazy dogs.\r\n'
1 u'The quick brown fox jumped over the lazy dogs.'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5\r\n'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5'

('t1-u8.txt', '')
raw: '\xef\xbb\xbfThe quick brown fox jumped over the lazy dogs.\r\n\xc3\xa0\xc3
\xa1\xc3\xa2\xc3\xa3\xc3\xa4\xc3\xa5\r\n'
guessed encoding: utf-8-sig
1 u'The quick brown fox jumped over the lazy dogs.\r\n'
1 u'The quick brown fox jumped over the lazy dogs.'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5\r\n'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5'

('t1-uc.txt', '')
raw: '\xff\xfeT\x00h\x00e\x00 \x00q\x00u\x00i\x00c\x00k\x00 \x00b\x00r\x00o\x00w
\x00n\x00 \x00f\x00o\x00x\x00 \x00j\x00u\x00m\x00p\x00e\x00d\x00 \x00o\x00v\x00e
\x00r\x00 \x00t\x00h\x00e\x00 \x00l\x00a\x00z\x00y\x00 \x00d\x00o\x00g\x00s\x00.
\x00\r\x00\n\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\r\x00\n\x00'
guessed encoding: utf-16
1 u'The quick brown fox jumped over the lazy dogs.\r\n'
1 u'The quick brown fox jumped over the lazy dogs.'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5\r\n'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5'

('t1-ucb.txt', '')
raw: '\xfe\xff\x00T\x00h\x00e\x00 \x00q\x00u\x00i\x00c\x00k\x00 \x00b\x00r\x00o\
x00w\x00n\x00 \x00f\x00o\x00x\x00 \x00j\x00u\x00m\x00p\x00e\x00d\x00 \x00o\x00v\
x00e\x00r\x00 \x00t\x00h\x00e\x00 \x00l\x00a\x00z\x00y\x00 \x00d\x00o\x00g\x00s\
x00.\x00\r\x00\n\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\r\x00\n'
guessed encoding: utf-16
1 u'The quick brown fox jumped over the lazy dogs.\r\n'
1 u'The quick brown fox jumped over the lazy dogs.'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5\r\n'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5'

Les choses à savoir :

(1) "mbcs" est un pseudo-encodage de système de fichiers qui n'a aucun rapport avec le décodage de l'image de l'utilisateur. contenu de fichiers. Sur un système où le codage par défaut est cp1252 il fait comme latin1 (aarrgghh !!); voir ci-dessous

>>> all_bytes = "".join(map(chr, range(256)))
>>> u1 = all_bytes.decode('cp1252', 'replace')
>>> u2 = all_bytes.decode('mbcs', 'replace')
>>> u1 == u2
False
>>> [(i, u1[i], u2[i]) for i in xrange(256) if u1[i] != u2[i]]
[(129, u'\ufffd', u'\x81'), (141, u'\ufffd', u'\x8d'), (143, u'\ufffd', u'\x8f')
, (144, u'\ufffd', u'\x90'), (157, u'\ufffd', u'\x9d')]
>>>

(2) chardet est très bon pour détecter les encodages basés sur des scripts non-latins (chinois/japonais/coréen, cyrillique, hébreu, grec) mais pas très bon pour les encodages basés sur le latin (Europe de l'Ouest/Centrale/Est, turc, vietnamien) et ne comprend pas du tout l'arabe.

3voto

kindall Points 60645

Le Bloc-notes enregistre les fichiers Unicode avec une marque d'ordre des octets. Cela signifie que les premiers octets du fichier seront :

  • EF BB BF -- UTF-8
  • FF FE -- "Unicode" (en fait UTF-16 little-endian, on dirait)
  • FE FF -- "Unicode big-endian" (ressemble à UTF-16 big-endian)

D'autres éditeurs de texte peuvent ou non avoir le même comportement, mais si vous êtes sûr que Notepad est utilisé, cela vous donnera une heuristique décente pour la sélection automatique de l'encodage. Cependant, toutes ces séquences sont également valables dans l'encodage ANSI, et il est donc possible que cette heuristique fasse des erreurs. Il n'est pas possible de garantir que le bon encodage est utilisé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X