¿Cómo determinar la encoding del texto?

Recibí un texto codificado, pero no sé qué juego de caracteres se usó. ¿Hay una manera de determinar la encoding de un archivo de texto usando Python? ¿Cómo puedo detectar la encoding / página de códigos de un archivo de texto relacionado con C #?

Detectar correctamente la encoding en todo momento es imposible .

(De Chardet FAQ 🙂

Sin embargo, algunas codificaciones están optimizadas para idiomas específicos, y los idiomas no son aleatorios. Algunas secuencias de caracteres aparecen todo el tiempo, mientras que otras secuencias no tienen sentido. Una persona que habla inglés con fluidez y que abre un periódico y encuentra “txzqJv 2! Dasd0a QqdKjvz” reconocerá instantáneamente que eso no es inglés (aunque está compuesto completamente de letras en inglés). Al estudiar una gran cantidad de texto “típico”, un algoritmo de computadora puede simular este tipo de fluidez y hacer una suposición educada sobre el lenguaje de un texto.

Existe la biblioteca chardet que utiliza ese estudio para intentar detectar la encoding. Chardet es un puerto del código de autodetección en Mozilla.

También puede utilizar UnicodeDammit . Intentará los siguientes métodos:

  • Una encoding descubierta en el propio documento: por ejemplo, en una statement XML o (para documentos HTML) una etiqueta META http-equiv. Si Beautiful Soup encuentra este tipo de encoding dentro del documento, vuelve a analizar el documento desde el principio y prueba la nueva encoding. La única excepción es si especificó explícitamente una encoding, y esa encoding realmente funcionó: entonces ignorará cualquier encoding que encuentre en el documento.
  • Una encoding detectada mirando los primeros bytes del archivo. Si se detecta una encoding en esta etapa, será una de las codificaciones UTF- *, EBCDIC o ASCII.
  • Una encoding olfateada por la biblioteca chardet , si la tiene instalada.
  • UTF-8
  • Windows-1252

Otra opción para resolver la encoding es usar libmagic (que es el código detrás del comando de archivo ). Hay una profusión de enlaces de python disponibles.

Los enlaces de python que se encuentran en el árbol de origen de archivos están disponibles como el paquete debian python-magic (o python3-magic ). Puede determinar la encoding de un archivo haciendo:

 import magic blob = open('unknown-file').read() m = magic.open(magic.MAGIC_MIME_ENCODING) m.load() encoding = m.buffer(blob) # "utf-8" "us-ascii" etc 

Hay un paquete pip de python-magic con nombre idéntico, pero incompatible en pypi que también usa libmagic . También puede obtener la encoding, haciendo:

 import magic blob = open('unknown-file').read() m = magic.Magic(mime_encoding=True) encoding = m.from_buffer(blob) 

Algunas estrategias de encoding, por favor descomprimir al gusto:

 #!/bin/bash # tmpfile=$1 echo '-- info about file file ........' file -i $tmpfile enca -g $tmpfile echo 'recoding ........' #iconv -f iso-8859-2 -t utf-8 back_test.xml > $tmpfile #enca -x utf-8 $tmpfile #enca -g $tmpfile recode CP1250..UTF-8 $tmpfile 

Es posible que desee verificar la encoding abriendo y leyendo el archivo en forma de un bucle … pero es posible que primero deba revisar el tamaño del archivo:

 encodings = ['utf-8', 'windows-1250', 'windows-1252' ...etc] for e in encodings: try: fh = codecs.open('file.txt', 'r', encoding=e) fh.readlines() fh.seek(0) except UnicodeDecodeError: print('got unicode error with %s , trying different encoding' % e) else: print('opening the file with encoding: %s ' % e) break 

Este es un ejemplo de lectura y toma de valor nominal de una predicción de encoding de chardet , lectura de n_lines del archivo en caso de que sea grande.

chardet también le da una probabilidad (es decir, confidence ) de su predicción de encoding (no he visto cómo lo lograron), que se devuelve con su predicción de chardet.predict() , por lo que podría trabajar de alguna manera si lo desea .

 def predict_encoding(file_path, n_lines=20): '''Predict a file's encoding using chardet''' import chardet # Open the file as binary data with open(file_path, 'rb') as f: # Join binary lines for specified number of lines rawdata = b''.join([f.readline() for _ in range(n_lines)]) return chardet.detect(rawdata)['encoding'] 
 # Function: OpenRead(file) # A text file can be encoded using: # (1) The default operating system code page, Or # (2) utf8 with a BOM header # # If a text file is encoded with utf8, and does not have a BOM header, # the user can manually add a BOM header to the text file # using a text editor such as notepad++, and rerun the python script, # otherwise the file is read as a codepage file with the # invalid codepage characters removed import sys if int(sys.version[0]) != 3: print('Aborted: Python 3.x required') sys.exit(1) def bomType(file): """ returns file encoding string for open() function EXAMPLE: bom = bomtype(file) open(file, encoding=bom, errors='ignore') """ f = open(file, 'rb') b = f.read(4) f.close() if (b[0:3] == b'\xef\xbb\xbf'): return "utf8" # Python automatically detects endianess if utf-16 bom is present # write endianess generally determined by endianess of CPU if ((b[0:2] == b'\xfe\xff') or (b[0:2] == b'\xff\xfe')): return "utf16" if ((b[0:5] == b'\xfe\xff\x00\x00') or (b[0:5] == b'\x00\x00\xff\xfe')): return "utf32" # If BOM is not provided, then assume its the codepage # used by your operating system return "cp1252" # For the United States its: cp1252 def OpenRead(file): bom = bomType(file) return open(file, 'r', encoding=bom, errors='ignore') ####################### # Testing it ####################### fout = open("myfile1.txt", "w", encoding="cp1252") fout.write("* hi there (cp1252)") fout.close() fout = open("myfile2.txt", "w", encoding="utf8") fout.write("\u2022 hi there (utf8)") fout.close() # this case is still treated like codepage cp1252 # (User responsible for making sure that all utf8 files # have a BOM header) fout = open("badboy.txt", "wb") fout.write(b"hi there. barf(\x81\x8D\x90\x9D)") fout.close() # Read Example file with Bom Detection fin = OpenRead("myfile1.txt") L = fin.readline() print(L) fin.close() # Read Example file with Bom Detection fin = OpenRead("myfile2.txt") L =fin.readline() print(L) #requires QtConsole to view, Cmd.exe is cp1252 fin.close() # Read CP1252 with a few undefined chars without barfing fin = OpenRead("badboy.txt") L =fin.readline() print(L) fin.close() # Check that bad characters are still in badboy codepage file fin = open("badboy.txt", "rb") fin.read(20) fin.close() 

Es, en principio, imposible determinar la encoding de un archivo de texto, en el caso general. Así que no, no hay una biblioteca estándar de Python para hacer eso por ti.

Si tiene un conocimiento más específico sobre el archivo de texto (por ejemplo, que es XML), puede haber funciones de biblioteca.

Si conoce el contenido del archivo, puede intentar decodificarlo con varias codificaciones y ver cuál falta. En general, no hay forma, ya que un archivo de texto es un archivo de texto y esos son estúpidos;)

Dependiendo de su plataforma, solo opto por usar el comando linux shell file . Esto me funciona, ya que lo estoy usando en un script que se ejecuta exclusivamente en una de nuestras máquinas Linux.

Obviamente, esta no es una solución o respuesta ideal, pero podría modificarse para adaptarse a sus necesidades. En mi caso, solo necesito determinar si un archivo es UTF-8 o no.

 import subprocess file_cmd = ['file', 'test.txt'] p = subprocess.Popen(file_cmd, stdout=subprocess.PIPE) cmd_output = p.stdout.readlines() # x will begin with the file type output as is observed using 'file' command x = cmd_output[0].split(": ")[1] return x.startswith('UTF-8')