Deux questions ici. J'ai un ensemble de fichiers qui sont généralement UTF-8 avec BOM. J'aimerais les convertir (idéalement en place) en UTF-8 sans nomenclature. Il semble que codecs.StreamRecoder(stream, encode, decode, Reader, Writer, errors)
s'en chargerait. Mais je ne vois pas vraiment de bons exemples d'utilisation. Serait-ce la meilleure façon de gérer cela?
source files:
Tue Jan 17$ file brh-m-157.json
brh-m-157.json: UTF-8 Unicode (with BOM) text
En outre, il serait idéal si nous pouvions gérer différents codages d’entrée sans le savoir explicitement (vu ASCII et UTF-16). Il semble que tout cela devrait être faisable. Existe-t-il une solution pouvant prendre n’importe quel codage Python connu et générer le format UTF-8 sans nomenclature?
edit 1 sol'n proposé d'en bas (merci!)
fp = open('brh-m-157.json','rw')
s = fp.read()
u = s.decode('utf-8-sig')
s = u.encode('utf-8')
print fp.encoding
fp.write(s)
Cela me donne l'erreur suivante:
IOError: [Errno 9] Bad file descriptor
On me dit dans les commentaires que l'erreur est d'ouvrir le fichier avec le mode 'rw' au lieu de 'r +'/'r + b'. Je devrais donc éventuellement modifier ma question et supprimer la partie résolue.
Utilisez simplement le codec "utf-8-sig" :
fp = open("file.txt")
s = fp.read()
u = s.decode("utf-8-sig")
Cela vous donne une chaîne unicode
sans la nomenclature. Vous pouvez alors utiliser
s = u.encode("utf-8")
pour obtenir une chaîne encodée en UTF-8 normale dans s
. Si vos fichiers sont volumineux, évitez de les lire tous en mémoire. La nomenclature se compose simplement de trois octets au début du fichier. Vous pouvez donc utiliser ce code pour les supprimer du fichier:
import os, sys, codecs
BUFSIZE = 4096
BOMLEN = len(codecs.BOM_UTF8)
path = sys.argv[1]
with open(path, "r+b") as fp:
chunk = fp.read(BUFSIZE)
if chunk.startswith(codecs.BOM_UTF8):
i = 0
chunk = chunk[BOMLEN:]
while chunk:
fp.seek(i)
fp.write(chunk)
i += len(chunk)
fp.seek(BOMLEN, os.SEEK_CUR)
chunk = fp.read(BUFSIZE)
fp.seek(-BOMLEN, os.SEEK_CUR)
fp.truncate()
Il ouvre le fichier, lit un morceau et l'écrit dans le fichier 3 octets plus tôt que celui où il l'a lu. Le fichier est réécrit sur place. Une solution plus simple consiste à écrire le fichier le plus court dans un nouveau fichier tel que la réponse de newtover . Ce serait plus simple, mais utilisez deux fois plus d’espace disque pendant une courte période.
Pour deviner l’encodage, vous pouvez simplement parcourir l’encodage du plus au moins spécifique:
def decode(s):
for encoding in "utf-8-sig", "utf-16":
try:
return s.decode(encoding)
except UnicodeDecodeError:
continue
return s.decode("latin-1") # will always work
Un fichier encodé en UTF-16 ne se décodera pas en UTF-8, nous essayons donc d'abord avec UTF-8. Si cela échoue, nous essayons avec UTF-16. Enfin, nous utilisons Latin-1 - cela fonctionnera toujours puisque tous les 256 octets sont des valeurs légales en Latin-1. Dans ce cas, vous voudrez peut-être renvoyer None
car il s'agit en réalité d'une solution de secours et votre code peut vouloir gérer cela plus attentivement (s'il le peut).
En Python 3, c'est très simple: lisez le fichier et réécrivez-le avec le codage utf-8
:
s = open(bom_file, mode='r', encoding='utf-8-sig').read()
open(bom_file, mode='w', encoding='utf-8').write(s)
import codecs
import shutil
import sys
s = sys.stdin.read(3)
if s != codecs.BOM_UTF8:
sys.stdout.write(s)
shutil.copyfileobj(sys.stdin, sys.stdout)
Ceci est mon implémentation pour convertir tout type d'encodage en UTF-8 sans nomenclature et remplacer les enlines par un format universel:
def utf8_converter(file_path, universal_endline=True):
'''
Convert any type of file to UTF-8 without BOM
and using universal endline by default.
Parameters
----------
file_path : string, file path.
universal_endline : boolean (True),
by default convert endlines to universal format.
'''
# Fix file path
file_path = os.path.realpath(os.path.expanduser(file_path))
# Read from file
file_open = open(file_path)
raw = file_open.read()
file_open.close()
# Decode
raw = raw.decode(chardet.detect(raw)['encoding'])
# Remove windows end line
if universal_endline:
raw = raw.replace('\r\n', '\n')
# Encode to UTF-8
raw = raw.encode('utf8')
# Remove BOM
if raw.startswith(codecs.BOM_UTF8):
raw = raw.replace(codecs.BOM_UTF8, '', 1)
# Write to file
file_open = open(file_path, 'w')
file_open.write(raw)
file_open.close()
return 0
J'ai trouvé cette question parce que j'avais des problèmes avec configparser.ConfigParser().read(fp)
lors de l'ouverture de fichiers avec un en-tête UTF8.
Pour ceux qui recherchent une solution pour supprimer l'en-tête afin que ConfigPhaser puisse ouvrir le fichier config au lieu de signaler une erreur de: File contains no section headers
, veuillez l'ouvrir de la manière suivante:
configparser.ConfigParser().read(config_file_path, encoding="utf-8-sig")
Cela pourrait vous épargner des efforts considérables en rendant inutile la suppression de l'en-tête de nomenclature du fichier.
(Je sais que cela ne semble pas être lié, mais j'espère que cela pourrait aider des personnes qui luttent comme moi.)
Vous pouvez utiliser des codecs.
import codecs
with open("test.txt",'r') as filehandle:
content = filehandle.read()
if content[:3] == codecs.BOM_UTF8:
content = content[3:]
print content.decode("utf-8")