Как определить кодировку текста?

Ответы:

225

Правильное определение кодировки все время невозможно .

(Из чарде FAQ)

Однако некоторые кодировки оптимизированы для конкретных языков, и языки не являются случайными. Некоторые последовательности символов появляются постоянно, тогда как другие последовательности не имеют смысла. Человек, свободно говорящий по-английски, который открывает газету и находит «txzqJv 2! Dasd0a QqdKjvz», сразу же поймет, что это не английский (хотя он полностью состоит из английских букв). Изучая много «типичного» текста, компьютерный алгоритм может имитировать этот вид беглости и сделать обоснованное предположение о языке текста.

Существует библиотека chardet, которая использует это исследование для определения кодировки. chardet - это порт кода автоопределения в Mozilla.

Вы также можете использовать UnicodeDammit . Он попробует следующие методы:

  • Кодировка обнаружена в самом документе: например, в объявлении XML или (для документов HTML) мета-тегом http-эквивалент. Если Beautiful Soup находит такую ​​кодировку в документе, он снова анализирует документ с самого начала и пробует новую кодировку. Единственное исключение - если вы явно указали кодировку, и эта кодировка действительно сработала: тогда она будет игнорировать любую кодировку, найденную в документе.
  • Кодировка, которую можно найти, просматривая первые несколько байтов файла. Если на этом этапе обнаружена кодировка, это будет одна из кодировок UTF- *, EBCDIC или ASCII.
  • Кодировка, обнаруженная библиотекой chardet , если она у вас установлена.
  • UTF-8,
  • Windows-1252
nosklo
источник
1
Спасибо за chardetссылку. Кажется хорошим, хотя и немного медленным.
Крейг МакКуин
17
@ Geomorillo: нет такой вещи как «стандарт кодирования». Кодирование текста является чем-то таким же старым, как вычисления, оно органично росло со временем и потребностями, не планировалось. «Юникод» - это попытка исправить это.
Носкло
1
И неплохо, учитывая все обстоятельства. Что я хотел бы знать, так это как узнать, с какой кодировкой был открыт открытый текстовый файл?
holdenweb
2
@dumbledad Я сказал, что правильно все время обнаружить это невозможно. Все, что вы можете сделать, - это догадаться, но иногда это может не сработать, оно не будет работать каждый раз из-за того, что кодировки на самом деле не обнаруживаются. Чтобы сделать предположение, вы можете использовать один из инструментов, которые я предложил в ответе
nosklo
1
@ LasseKärkkäinen точка этого ответа, чтобы показать , что corectly обнаружения кодирования невозможно ; Функция, которую вы предоставляете, может угадать для вашего случая, но во многих случаях это неправильно.
Носкло
68

Другим вариантом для разработки кодировки является использование libmagic (код, стоящий за командой file ). Существует множество доступных привязок Python.

Привязки python, которые находятся в дереве исходных файлов, доступны в виде пакета debian python-magic (или python3-magic ). Он может определить кодировку файла, выполнив:

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

Существует также одноименный , но несовместимый пакет python-magic pip для pypi, который также использует libmagic. Он также может получить кодировку, выполнив:

import magic

blob = open('unknown-file', 'rb').read()
m = magic.Magic(mime_encoding=True)
encoding = m.from_buffer(blob)
Хэмиш Даунер
источник
5
libmagicдействительно жизнеспособная альтернатива chardet. И отличная информация о названных пакетах python-magic! Я уверен, что эта двусмысленность кусает многих людей
MestreLion
1
fileне особенно хорош в идентификации человеческого языка в текстовых файлах. Он отлично подходит для идентификации различных форматов контейнеров, хотя иногда вам нужно знать, что это значит («документ Microsoft Office» может означать сообщение Outlook и т. Д.).
tripleee
Ища способ управления загадкой кодировки файлов я нашел этот пост. К сожалению, используя пример кода, я не могу пройти мимо open(): UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfc in position 169799: invalid start byte. Файл кодирования согласно Vim, :set fileencodingесть latin1.
кстианский
Если я использую необязательный аргумент errors='ignore', вывод кода примера будет менее полезным binary.
кстианский
2
@xtian Вам нужно открыть в двоичном режиме, то есть открыть ("filename.txt", "rb").
Л.
31

Некоторые стратегии кодирования, пожалуйста, раскомментируйте по вкусу:

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

Возможно, вы захотите проверить кодировку, открыв и прочитав файл в форме цикла ... но вам может понадобиться сначала проверить размер файла:

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              
zzart
источник
Вы также можете использовать io, как io.open(filepath, 'r', encoding='utf-8'), что более удобно, потому codecsчто не конвертируется \nавтоматически при чтении и записи. Подробнее ЗДЕСЬ
Searene
23

Вот пример чтения и принятия за чистую монету chardetпредсказания кодирования, считывания n_linesиз файла, если он большой.

chardetтакже дает вам вероятность (т. е. confidence) его прогнозирования кодирования (не смотрел, как они с этим придумали), который возвращается с его прогнозом chardet.predict(), так что вы можете как-то сработать, если хотите.

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']
ryanjdillon
источник
Посмотрите на это после того, как вы проголосовали, и теперь видите, что это решение может замедлиться, если в первой строке будет много данных. В некоторых случаях было бы лучше читать данные по-разному.
ryanjdillon
2
Я изменил эту функцию следующим образом: def predict_encoding(file_path, n=20): ... skip ... and then rawdata = b''.join([f.read() for _ in range(n)]) пробовал эту функцию на Python 3.6, отлично работал с кодировками "ascii", "cp1252", "utf-8", "unicode". Так что это определенно upvote
n158
1
это очень хорошо для обработки небольших наборов данных с различными форматами. Протестировал это рекурсивно на моем корневом каталоге, и это сработало как удовольствие. Спасибо дружище.
Датановице
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

В зависимости от вашей платформы, я просто выбираю команду оболочки linux file. Это работает для меня, так как я использую его в сценарии, который работает исключительно на одной из наших машин Linux.

Очевидно, что это не идеальное решение или ответ, но его можно изменить в соответствии с вашими потребностями. В моем случае мне просто нужно определить, является ли файл UTF-8 или нет.

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')
MikeD
источник
Форкинг нового процесса не нужен. Код Python уже выполняется внутри процесса и может сам вызывать надлежащие системные функции без дополнительных затрат на загрузку нового процесса.
vdboor
2

Это может быть полезно

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'
richinex
источник
1

В принципе, невозможно определить кодировку текстового файла в общем случае. Так что нет, для этого нет стандартной библиотеки Python.

Если у вас есть более конкретные знания о текстовом файле (например, о том, что это XML), возможно, существуют библиотечные функции.

Мартин против Лёвиса
источник
1

Если вам известно какое-то содержимое файла, вы можете попытаться расшифровать его с помощью нескольких кодировок и посмотреть, что в нем отсутствует. В общем, нет никакого способа, так как текстовый файл является текстовым файлом, и это глупо;)

Мартин Турау
источник
1

На этом сайте есть код Python для распознавания ascii, кодирования с помощью boms и utf8 без bom: https://unicodebook.readthedocs.io/guess_encoding.html . Считайте файл в байтовый массив (данные): http://www.codecodex.com/wiki/Read_a_file_into_a_byte_array . Вот пример. Я в оссе.

#!/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
js2010
источник
Ссылка на решение приветствуется, но, пожалуйста, убедитесь, что ваш ответ будет полезен без него: добавьте контекст вокруг ссылки, чтобы ваши коллеги-пользователи поняли, что это такое и почему, а затем процитируйте наиболее релевантную часть страницы, которую вы Повторная ссылка на случай, если целевая страница недоступна. Ответы, которые немного больше, чем ссылка, могут быть удалены.
двойной гудок