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


220

Я получил некоторый текст в кодировке, но я не знаю, какая кодировка была использована. Есть ли способ определить кодировку текстового файла с помощью Python? Как я могу определить кодировку / кодовую страницу текстового файла имеет дело с C #.

Ответы:


225

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

(Из чарде FAQ)

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

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

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

  • Кодировка обнаружена в самом документе: например, в объявлении XML или (для документов HTML) мета-тегом http-эквивалент. Если Beautiful Soup находит такую ​​кодировку в документе, он снова анализирует документ с самого начала и пробует новую кодировку. Единственное исключение - если вы явно указали кодировку, и эта кодировка действительно сработала: тогда она будет игнорировать любую кодировку, найденную в документе.
  • Кодировка, которую можно найти, просматривая первые несколько байтов файла. Если на этом этапе обнаружена кодировка, это будет одна из кодировок UTF- *, EBCDIC или ASCII.
  • Кодировка, обнаруженная библиотекой chardet , если она у вас установлена.
  • UTF-8,
  • Windows-1252

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              

Вы также можете использовать 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

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

Форкинг нового процесса не нужен. Код 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'

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

Ссылка на решение приветствуется, но, пожалуйста, убедитесь, что ваш ответ будет полезен без него: добавьте контекст вокруг ссылки, чтобы ваши коллеги-пользователи поняли, что это такое и почему, а затем процитируйте наиболее релевантную часть страницы, которую вы Повторная ссылка на случай, если целевая страница недоступна. Ответы, которые немного больше, чем ссылка, могут быть удалены.
— двойной гудок
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.