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.