Come determinare la codifica del testo?


Risposte:


225

Rilevare correttamente la codifica in ogni momento è impossibile .

(Da Chardet FAQ :)

Tuttavia, alcune codifiche sono ottimizzate per lingue specifiche e le lingue non sono casuali. Alcune sequenze di personaggi compaiono continuamente, mentre altre sequenze non hanno senso. Una persona fluente in inglese che apre un giornale e trova "txzqJv 2! Dasd0a QqdKjvz" riconoscerà immediatamente che non è inglese (anche se è composto interamente da lettere inglesi). Studiando un sacco di testo "tipico", un algoritmo informatico può simulare questo tipo di fluidità e fare un'ipotesi educata sulla lingua di un testo.

C'è la libreria chardet che usa quello studio per provare a rilevare la codifica. chardet è una porta del codice di rilevamento automatico in Mozilla.

Puoi anche usare UnicodeDammit . Proverà i seguenti metodi:

  • Una codifica scoperta nel documento stesso: ad esempio, in una dichiarazione XML o (per documenti HTML) un tag META http-equiv. Se Beautiful Soup trova questo tipo di codifica all'interno del documento, analizza nuovamente il documento dall'inizio e prova la nuova codifica. L'unica eccezione è se hai specificato esplicitamente una codifica e quella codifica ha funzionato effettivamente: ignorerà qualsiasi codifica trovata nel documento.
  • Una codifica sniffata guardando i primi byte del file. Se in questa fase viene rilevata una codifica, sarà una delle codifiche UTF- *, EBCDIC o ASCII.
  • Una codifica sniffata dalla libreria chardet , se installata.
  • UTF-8
  • Windows-1252

1
Grazie per il chardetriferimento. Sembra buono, anche se un po 'lento.
Craig McQueen,

17
@Geomorillo: Non esiste "lo standard di codifica". La codifica del testo è vecchia quanto l'informatica, è cresciuta organicamente con il tempo e le esigenze, non è stata pianificata. "Unicode" è un tentativo di risolvere questo problema.
nosklo,

1
E non male, tutto sommato. Quello che vorrei sapere è, come faccio a sapere con quale codifica è stato aperto un file di testo aperto?
holdenweb

2
@dumbledad quello che ho detto è che rilevarlo correttamente tutte le volte è impossibile. Tutto quello che puoi fare è un'ipotesi, ma a volte può fallire, non funzionerà ogni volta, a causa della codifica non rilevabile. Per indovinare, puoi usare uno degli strumenti che ho suggerito nella risposta
nosklo,

1
@ LasseKärkkäinen il punto di questa risposta è dimostrare che è impossibile rilevare correttamente la codifica ; la funzione che fornisci può indovinare il caso, ma in molti casi è errata.
nosklo

67

Un'altra opzione per elaborare la codifica è usare libmagic (che è il codice dietro il comando file ). È disponibile una profusione di attacchi in pitone.

I collegamenti python che vivono nell'albero dei file sorgente sono disponibili come pacchetto debian python-magic (o python3-magic ). Può determinare la codifica di un file facendo:

import magic

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

Esiste un pacchetto pip python-magic identico, ma incompatibile, su pypi che usa anche libmagic. Può anche ottenere la codifica, facendo:

import magic

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

5
libmagicè davvero una valida alternativa a chardet. E ottime informazioni sui pacchetti distinti chiamati python-magic! Sono sicuro che questa ambiguità morde molte persone
MestreLion,

1
filenon è particolarmente bravo a identificare il linguaggio umano nei file di testo. È eccellente per identificare vari formati di container, anche se a volte devi sapere cosa significa ("documento di Microsoft Office" potrebbe significare un messaggio di Outlook, ecc.).
Tripleee

Alla ricerca di un modo per gestire il mistero della codifica dei file ho trovato questo post. Purtroppo, utilizzando il codice di esempio, non posso andare oltre open(): UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfc in position 169799: invalid start byte. La codifica dei file secondo vim's :set fileencodingè latin1.
xtian il

Se utilizzo l'argomento facoltativo errors='ignore', l'output del codice di esempio è meno utile binary.
xtian il

2
@xtian È necessario aprire in modalità binaria, ovvero open ("nomefile.txt", "rb").
L. Kärkkäinen,

31

Alcune strategie di codifica, ti preghiamo di commentare a piacere:

#!/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

Potresti voler controllare la codifica aprendo e leggendo il file in una forma di ciclo ... ma potresti dover prima controllare la dimensione del file:

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              

Puoi anche usare io, come io.open(filepath, 'r', encoding='utf-8'), che è più conveniente, perché codecsnon si converte \nautomaticamente in lettura e scrittura. Altro su QUI
Searene,

23

Ecco un esempio di lettura e presa al valore nominale di una chardetprevisione di codifica, lettura n_linesdal file nel caso in cui sia grande.

chardetti dà anche una probabilità (cioè confidence) della sua previsione di codifica (non ho visto come ne escono), che viene restituito con la sua previsione da chardet.predict(), quindi potresti farlo in qualche modo se ti piace.

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']

Guardando questo dopo aver ottenuto un voto positivo e ora vediamo che questa soluzione potrebbe rallentare se ci fossero molti dati sulla prima riga. In alcuni casi sarebbe meglio leggere i dati in modo diverso.
Ryandillon,

2
Ho modificato questa funzione in questo modo: def predict_encoding(file_path, n=20): ... skip ... and then rawdata = b''.join([f.read() for _ in range(n)]) ho provato questa funzione su Python 3.6, ha funzionato perfettamente con le codifiche "ascii", "cp1252", "utf-8", "unicode". Quindi questo è decisamente positivo.
n.

1
questo è ottimo per gestire piccoli set di dati con una varietà di formati. Ho provato questo in modo ricorsivo sulla mia directory principale e ha funzionato come un piacere. Grazie compagno.
Datanovice,

4
# 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()

2

A seconda della tua piattaforma, scelgo di usare il filecomando shell linux . Questo funziona per me dal momento che lo sto usando in uno script che gira esclusivamente su una delle nostre macchine Linux.

Ovviamente questa non è una soluzione o una risposta ideale, ma potrebbe essere modificata in base alle proprie esigenze. Nel mio caso, devo solo determinare se un file è UTF-8 o meno.

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')

Non è necessario il fork di un nuovo processo. Il codice Python viene già eseguito all'interno di un processo e può chiamare le funzioni di sistema appropriate senza l'overhead del caricamento di un nuovo processo.
vdboor,

2

Questo potrebbe essere utile

from bs4 import UnicodeDammit
with open('automate_data/billboard.csv', 'rb') as file:
   content = file.read()

suggestion = UnicodeDammit(content)
suggestion.original_encoding
#'iso-8859-1'

1

In linea di principio è impossibile determinare la codifica di un file di testo, nel caso generale. Quindi no, non esiste una libreria Python standard per farlo.

Se si dispone di conoscenze più specifiche sul file di testo (ad esempio che si tratta di XML), potrebbero esserci funzioni di libreria.


1

Se conosci il contenuto del file, puoi provare a decodificarlo con diverse codifiche e vedere quale manca. In generale non c'è modo poiché un file di testo è un file di testo e quelli sono stupidi;)


1

Questo sito ha un codice Python per il riconoscimento di ASCII, la codifica con boms e utf8 no bom: https://unicodebook.readthedocs.io/guess_encoding.html . Leggi il file nell'array di byte (dati): http://www.codecodex.com/wiki/Read_a_file_into_a_byte_array . Ecco un esempio Sono in osx.

#!/usr/bin/python                                                                                                  

import sys

def isUTF8(data):
    try:
        decoded = data.decode('UTF-8')
    except UnicodeDecodeError:
        return False
    else:
        for ch in decoded:
            if 0xD800 <= ord(ch) <= 0xDFFF:
                return False
        return True

def get_bytes_from_file(filename):
    return open(filename, "rb").read()

filename = sys.argv[1]
data = get_bytes_from_file(filename)
result = isUTF8(data)
print(result)


PS /Users/js> ./isutf8.py hi.txt                                                                                     
True

Un link a una soluzione è il benvenuto, ma assicurati che la tua risposta sia utile senza di essa: aggiungi un contesto attorno al link in modo che i tuoi colleghi utenti abbiano qualche idea di cosa sia e perché sia ​​lì, quindi cita la parte più pertinente della pagina che ' re collegamento a nel caso in cui la pagina di destinazione non sia disponibile. Le risposte che sono poco più di un collegamento possono essere eliminate.
doppio segnale acustico
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.