Convertir UTF-8 avec nomenclature en UTF-8 sans nomenclature en Python
Deux questions ici. J'ai un ensemble de fichiers qui sont généralement UTF-8 avec BOM. Je voudrais les convertir (idéalement en place) en UTF-8 sans nomenclature. Il semble que codecs.StreamRecoder(stream, encode, decode, Reader, Writer, errors)
gérerait cela. Mais je ne vois pas vraiment de bons exemples sur l'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 encodages d'entrée sans le savoir explicitement (vu ASCII et UTF-16). Il semble que tout cela devrait être faisable. Est-il une solution qui peut prenez n'importe quel encodage Python connu et sortie en UTF-8 sans nomenclature?
Edit 1 sol'n proposé par le 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
Newsflash
On me dit dans les commentaires que l'erreur est que j'ouvre le fichier avec le mode 'rw' au lieu de 'r+' / 'r + b', donc je devrais éventuellement rééditer ma question et supprimer la partie résolue.
6 réponses
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 ensuite utiliser
s = u.encode("utf-8")
Pour récupérer une chaîne codée UTF-8 normale dans s
. Si vos fichiers sont volumineux, vous devriez éviter de les lire tous en mémoire. La nomenclature est 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 celui où il le lire. Le fichier est réécrit sur place. La solution la plus simple consiste à écrire le fichier le plus court dans un nouveau fichier comme la réponse de newtover. Ce serait plus simple, mais utilisez deux fois l'espace disque pendant une courte période.
Quant à deviner l'encodage, alors 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 décodera pas en UTF-8, donc nous essayons D'abord avec UTF-8. Si cela échoue, alors 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. Vous voudrez peut-être retourner None
à la place dans ce cas, car c'est vraiment une solution de repli et votre code voudra peut-être gérer cela plus attentivement (si c'est possible).
En Python 3, c'est assez simple: lisez le fichier et réécrivez-le avec utf-8
encodage:
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)
C'est mon implémentation pour convertir n'importe quel type d'encodage en UTF-8 sans nomenclature et remplacer Windows 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
Vous pouvez utiliser des codecs.
import codecs
content = open("test.txt",'r').read()
filehandle.close()
if content[:3] == codecs.BOM_UTF8
content = content[3:]
print content.decode("utf-8")
J'ai trouvé cette question parce que j'ai des problèmes avec configparser.ConfigParser().read(fp)
lors de l'ouverture de fichiers avec L'en-tête de nomenclature UTF8.
Pour ceux qui recherchent une solution pour supprimer L'en-tête afin que ConfigPhaser puisse ouvrir le fichier de configuration au lieu de signaler une erreur de:
File contains no section headers
, veuillez ouvrir le fichier comme suit:
configparser.ConfigParser().read(config_file_path, encoding="utf-8-sig")
Cela pourrait vous faire économiser des tonnes d'efforts en rendant inutile la suppression de l'en-tête de nomenclature du fichier.
(je sais que cela ne semble pas lié, mais j'espère que cela pourrait aider les gens qui luttent comme moi.)