bytes bom python utf-8 utf-16 byte-order-mark

python - bom - utf-16 vs utf-8



Convierta UTF-8 con BOM a UTF-8 sin BOM en Python (6)

Dos preguntas aquí. Tengo un conjunto de archivos que generalmente son UTF-8 con BOM. Me gustaría convertirlos (idealmente en su lugar) a UTF-8 sin BOM. Parece que codecs.StreamRecoder(stream, encode, decode, Reader, Writer, errors) manejaría esto. Pero realmente no veo buenos ejemplos de uso. ¿Esta sería 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 una codificación de entrada diferente sin conocer explícitamente (se ve ASCII y UTF-16). Parece que esto debería ser factible. ¿Hay alguna solución que pueda tomar cualquier codificación y salida de Python conocida como UTF-8 sin BOM?

edita 1 propuesta sol''n 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

Noticia de última hora

Me han dicho en comentarios que el error es abrir 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.


En Python 3 es bastante fácil: lea el archivo y vuelva a escribirlo con la codificación utf-8 :

s = open(bom_file, mode=''r'', encoding=''utf-8-sig'').read() open(bom_file, mode=''w'', encoding=''utf-8'').write(s)


Encontré esta pregunta porque tenía problemas con configparser.ConfigParser().read(fp) al abrir archivos con el encabezado BOM UTF8.

Para aquellos que están buscando una solución para eliminar el encabezado para que ConfigPhaser pueda abrir el archivo de configuración en lugar de informar un error de: El File contains no section headers , abra el archivo como el siguiente:

configparser.ConfigParser().read(config_file_path, encoding="utf-8-sig")

Esto podría ahorrarle toneladas de esfuerzo al hacer que la eliminación del encabezado BOM del archivo sea innecesaria.

(Sé que esto no parece estar relacionado, pero espero que esto pueda ayudar a las personas que luchan como yo).


Esta es mi implementación para convertir cualquier tipo de codificación a UTF-8 sin BOM y reemplazar en línea de Windows 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


Puedes usar 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")


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 cadena unicode sin la lista de materiales. Puedes usar

s = u.encode("utf-8")

para obtener una cadena codificada UTF-8 normal en s . Si sus archivos son grandes, entonces debe evitar leerlos todos en la memoria. La lista de materiales es 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 que donde lo leyó. El archivo se reescribe en el lugar. Como solución más fácil es escribir el archivo más corto en un nuevo archivo 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 la adivinación de la codificación, puede recorrer la codificación de más a menos específico:

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, por lo que primero probamos con UTF-8. Si eso falla, entonces probamos con UTF-16. Finalmente, usamos Latin-1 - esto siempre funcionará ya que todos los 256 bytes son valores legales en Latin-1. Es posible que desee devolver None en este caso, ya que es realmente una alternativa y su código podría querer manejar esto más cuidadosamente (si es que puede).


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)