Aquí dos preguntas. Tengo un conjunto de archivos que suelen ser UTF-8 con BOM. Me gustaría convertirlos (idealmente en su lugar) a UTF-8 sin BOM. Parece codecs.StreamRecoder(stream, encode, decode, Reader, Writer, errors)
que manejaría esto. Pero realmente no veo buenos ejemplos de uso. ¿Sería esta la mejor manera de manejar esto?
source files:
Tue Jan 17$ file brh-m-157.json
brh-m-157.json: UTF-8 Unicode (with BOM) text
Además, sería ideal si pudiéramos manejar diferentes codificaciones de entrada sin saberlo explícitamente (visto ASCII y UTF-16). Parece que todo esto debería ser factible. ¿Existe una solución que pueda tomar cualquier codificación y salida de Python conocida como UTF-8 sin BOM?
editar 1 sol'n propuesto desde abajo (¡gracias!)
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)
Esto me da el siguiente error:
IOError: [Errno 9] Bad file descriptor
Noticias de última hora
En los comentarios me dicen que el error es que abro el archivo con el modo 'rw' en lugar de 'r +' / 'r + b', por lo que eventualmente debería volver a editar mi pregunta y eliminar la parte resuelta.
fuente
r+
modo. Agregueb
también para que funcione también en Windows sin ningún negocio divertido que termine la línea. Finalmente, querrá volver al principio del archivo y truncarlo al final; consulte mi respuesta actualizada.Respuestas:
Simplemente use el códec "utf-8-sig" :
fp = open("file.txt") s = fp.read() u = s.decode("utf-8-sig")
Eso le da una
unicode
cadena sin la lista de materiales. Luego puede usars = u.encode("utf-8")
para recuperar una cadena codificada en UTF-8 normal
s
. Si sus archivos son grandes, debe evitar leerlos todos en la memoria. La lista de materiales tiene simplemente tres bytes al principio del archivo, por lo que puede usar este código para eliminarlos del archivo: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()
Abre el archivo, lee un fragmento y lo escribe en el archivo 3 bytes antes de donde lo leyó. El archivo se reescribe in situ. Una solución más fácil es escribir el archivo más corto en un archivo nuevo como la respuesta de newtover . Eso sería más simple, pero use el doble de espacio en disco durante un período corto.
En cuanto a adivinar la codificación, puede recorrer la codificación de la más a la menos específica:
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 archivo codificado en UTF-16 no se decodificará como UTF-8, así que probamos primero con UTF-8. Si eso falla, lo intentamos con UTF-16. Finalmente, usamos Latin-1; esto siempre funcionará ya que los 256 bytes son valores legales en Latin-1. Es posible que desee volver
None
en su lugar en este caso, ya que en realidad es una alternativa y es posible que su código quiera manejar esto con más cuidado (si puede).fuente
En Python 3 es bastante fácil: lea el archivo y vuelva a escribirlo con
utf-8
codificación:s = open(bom_file, mode='r', encoding='utf-8-sig').read() open(bom_file, mode='w', encoding='utf-8').write(s)
fuente
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)
fuente
header = header[3:] if header[0:3] == codecs.BOM_UTF8 else header
Esta es mi implementación para convertir cualquier tipo de codificación a UTF-8 sin BOM y reemplazar Windows enlines por formato universal:
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
fuente
Puede utilizar códecs.
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")
fuente
Encontré esta pregunta porque tuve problemas
configparser.ConfigParser().read(fp)
al abrir archivos con el encabezado BOM UTF8.Para aquellos que buscan una solución para eliminar el encabezado para que ConfigPhaser pueda abrir el archivo de configuración en lugar de informar un error de:,
File contains no section headers
abra el archivo de la siguiente manera:configparser.ConfigParser().read(config_file_path, encoding="utf-8-sig")
Esto podría ahorrarle mucho esfuerzo al hacer que la eliminación del encabezado de la lista de materiales del archivo sea innecesaria.
(Sé que esto no suena relacionado, pero espero que esto pueda ayudar a las personas que luchan como yo).
fuente