Comment déterminer l'encodage du texte?


Réponses:


225

Il est impossible de détecter correctement l'encodage à tout moment .

(De la chardet FAQ :)

Cependant, certains encodages sont optimisés pour des langues spécifiques et les langues ne sont pas aléatoires. Certaines séquences de personnages apparaissent tout le temps, tandis que d'autres séquences n'ont aucun sens. Une personne parlant couramment l'anglais qui ouvre un journal et trouve «txzqJv 2! Dasd0a QqdKjvz» reconnaîtra instantanément que ce n'est pas de l'anglais (même s'il est entièrement composé de lettres anglaises). En étudiant beaucoup de texte «typique», un algorithme informatique peut simuler ce type de maîtrise et faire une supposition éclairée sur la langue d'un texte.

Il y a la bibliothèque chardet qui utilise cette étude pour essayer de détecter l'encodage. chardet est un portage du code de détection automatique dans Mozilla.

Vous pouvez également utiliser UnicodeDammit . Il essaiera les méthodes suivantes:

  • Un encodage découvert dans le document lui-même: par exemple, dans une déclaration XML ou (pour les documents HTML) une balise META http-equiv. Si Beautiful Soup trouve ce type d'encodage dans le document, il analyse à nouveau le document depuis le début et essaie le nouvel encodage. La seule exception est si vous avez explicitement spécifié un encodage, et que l'encodage a réellement fonctionné: alors il ignorera tout encodage qu'il trouve dans le document.
  • Un encodage reniflé en regardant les premiers octets du fichier. Si un codage est détecté à ce stade, ce sera l'un des codages UTF- *, EBCDIC ou ASCII.
  • Un encodage reniflé par le chardet bibliothèque , si vous l'avez installé.
  • UTF-8
  • Windows-1252

1
Merci pour la chardetréférence. Semble bien, bien qu'un peu lent.
Craig McQueen

17
@Geomorillo: Il n'y a rien de tel que "la norme de codage". L'encodage de texte est quelque chose d'aussi ancien que l'informatique, il s'est développé de manière organique avec le temps et les besoins, il n'était pas prévu. "Unicode" est une tentative pour résoudre ce problème.
nosklo

1
Et pas mal, tout bien considéré. Ce que je voudrais savoir, c'est comment savoir avec quel encodage un fichier texte ouvert a été ouvert?
holdenweb

2
@dumbledad ce que j'ai dit, c'est qu'il est impossible de le détecter correctement à tout moment . Tout ce que vous pouvez faire est une supposition, mais cela peut parfois échouer, cela ne fonctionnera pas à chaque fois, car les encodages ne sont pas vraiment détectables. Pour faire la supposition, vous pouvez utiliser l'un des outils que j'ai suggérés dans la réponse
nosklo

1
@ LasseKärkkäinen le but de cette réponse est de montrer qu'il est impossible de détecter correctement l'encodage ; la fonction que vous fournissez peut être appropriée à votre cas, mais elle est incorrecte dans de nombreux cas.
nosklo

67

Une autre option pour travailler sur l'encodage est d'utiliser libmagic (qui est le code derrière la commande file ). Il existe une profusion de liaisons python disponibles.

Les liaisons python présentes dans l'arborescence des sources de fichiers sont disponibles en tant que paquet Debian python-magic (ou python3-magic ). Il peut déterminer l'encodage d'un fichier en faisant:

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

Il y a un paquet pip python-magic de nom identique, mais incompatible, sur pypi qui utilise également libmagic. Il peut également obtenir l'encodage en faisant:

import magic

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

5
libmagicest en effet une alternative viable à chardet. Et d'excellentes informations sur les différents packages nommés python-magic! Je suis sûr que cette ambiguïté pique beaucoup de monde
MestreLion

1
filen'est pas particulièrement bon pour identifier le langage humain dans les fichiers texte. Il est excellent pour identifier différents formats de conteneurs, même si vous devez parfois savoir ce que cela signifie («document Microsoft Office» peut signifier un message Outlook, etc.).
tripleee

À la recherche d'un moyen de gérer le mystère de l'encodage des fichiers, j'ai trouvé ce message. Malheureusement, en utilisant le code exemple, je ne peux pas aller au- delà open(): UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfc in position 169799: invalid start byte. Le codage du fichier selon vim :set fileencodingest latin1.
xtian

Si j'utilise l'argument facultatif errors='ignore', la sortie de l'exemple de code est la moins utile binary.
xtian

2
@xtian Vous devez ouvrir en mode binaire, c'est-à-dire ouvrir ("filename.txt", "rb").
L. Kärkkäinen

31

Quelques stratégies d'encodage, veuillez ne pas commenter au goût:

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

Vous voudrez peut-être vérifier l'encodage en ouvrant et en lisant le fichier sous la forme d'une boucle ... mais vous devrez peut-être d'abord vérifier la taille du fichier:

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              

Vous pouvez également utiliser io, comme io.open(filepath, 'r', encoding='utf-8'), ce qui est plus pratique, car il codecsne se convertit pas \nautomatiquement en lecture et en écriture. Plus d'informations ICI
Searene

23

Voici un exemple de lecture et de prise à la valeur nominale d'une chardetprédiction de codage, lecture n_linesdu fichier dans le cas où il est volumineux.

chardetvous donne également une probabilité (c'est-à-dire confidence) de sa prédiction de codage (je n'ai pas regardé comment ils en sont arrivés à cela), qui est retournée avec sa prédiction chardet.predict(), vous pouvez donc travailler cela d'une manière ou d'une autre si vous le souhaitez.

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

En regardant cela après avoir obtenu un vote positif et voyez maintenant que cette solution pourrait ralentir s'il y avait beaucoup de données sur la première ligne. Dans certains cas, il serait préférable de lire les données différemment.
ryanjdillon

2
J'ai modifié cette fonction de cette façon: def predict_encoding(file_path, n=20): ... skip ... and then rawdata = b''.join([f.read() for _ in range(n)]) J'ai essayé cette fonction sur Python 3.6, fonctionnait parfaitement avec les encodages "ascii", "cp1252", "utf-8", "unicode". C'est donc définitivement un vote positif.
n158

1
c'est très bon pour gérer de petits ensembles de données avec une variété de formats. J'ai testé cela récursivement sur mon répertoire racine et cela a fonctionné comme un régal. Merci mon pote.
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

En fonction de votre plateforme, je choisis simplement d'utiliser la filecommande shell linux . Cela fonctionne pour moi car je l'utilise dans un script qui s'exécute exclusivement sur l'une de nos machines Linux.

Évidemment, ce n'est pas une solution ou une réponse idéale, mais elle pourrait être modifiée pour répondre à vos besoins. Dans mon cas, je dois juste déterminer si un fichier est UTF-8 ou non.

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

Il n'est pas nécessaire de bifurquer un nouveau processus. Le code Python s'exécute déjà dans un processus et peut appeler lui-même les fonctions système appropriées sans avoir à surcharger le chargement d'un nouveau processus.
vdboor

2

Cela pourrait être 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

Il est, en principe, impossible de déterminer l'encodage d'un fichier texte, dans le cas général. Donc non, il n'y a pas de bibliothèque Python standard pour le faire.

Si vous avez des connaissances plus spécifiques sur le fichier texte (par exemple qu'il est XML), il peut y avoir des fonctions de bibliothèque.


1

Si vous connaissez le contenu du fichier, vous pouvez essayer de le décoder avec plusieurs encodages et voir celui qui manque. En général, il n'y a aucun moyen car un fichier texte est un fichier texte et ceux-ci sont stupides;)


1

Ce site a du code python pour reconnaître l'ascii, l'encodage avec des boms et utf8 no bom: https://unicodebook.readthedocs.io/guess_encoding.html . Lire le fichier dans un tableau d'octets (données): http://www.codecodex.com/wiki/Read_a_file_into_a_byte_array . Voici un exemple. Je suis en 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 lien vers une solution est le bienvenu, mais assurez-vous que votre réponse est utile sans lui: ajoutez du contexte autour du lien pour que vos collègues aient une idée de ce que c'est et pourquoi il est là, puis citez la partie la plus pertinente de la page que vous '' relier à au cas où la page cible n'est pas disponible. Les réponses qui ne sont guère plus qu'un lien peuvent être supprimées.
double bip le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.