How to determine the encoding of text?
Я получил некоторый текст в кодировке, но я не знаю, какая кодировка была использована. Есть ли способ определить кодировку текстового файла с помощью Python? Как я могу определить кодировку / кодовую страницу текстового файла имеет дело с C#.
16 ответов
Правильное определение кодировки за все время невозможно.
(Из чарде FAQ)
Однако некоторые кодировки оптимизированы для конкретных языков, и языки не являются случайными. Некоторые последовательности символов появляются постоянно, в то время как другие последовательности не имеют смысла. Человек, свободно говорящий по-английски, который открывает газету и находит "txzqJv 2! Dasd0a QqdKjvz", сразу же поймет, что это не английский (хотя он полностью состоит из английских букв). Изучая много "типичного" текста, компьютерный алгоритм может имитировать этот вид беглости и сделать обоснованное предположение о языке текста.
Существует библиотека chardet, которая использует это исследование для определения кодировки. chardet - это порт кода автоопределения в Mozilla.
Вы также можете использовать UnicodeDammit. Он попробует следующие методы:
- Кодировка обнаружена в самом документе: например, в декларации XML или (для документов HTML) мета-теге http-эквивалент. Если Beautiful Soup находит кодировку такого типа в документе, он снова анализирует документ с самого начала и пробует новую кодировку. Единственное исключение - если вы явно указали кодировку, и эта кодировка действительно сработала: тогда она будет игнорировать любую кодировку, найденную в документе.
- Кодировка, которую можно найти, просматривая первые несколько байтов файла. Если на этом этапе обнаружена кодировка, это будет одна из кодировок UTF-*, EBCDIC или ASCII.
- Кодировка, обнаруженная библиотекой chardet, если она у вас установлена.
- UTF-8,
- Windows-1252
Другой вариант для работы с кодировкой - использовать libmagic (код, стоящий за командой file). Доступно множество привязок Python.
Привязки python, которые находятся в дереве исходных файлов, доступны в виде пакета debian python-magic (или python3-magic). Он может определить кодировку файла, выполнив:
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
На pypi есть пакет с pip -магическим pip с одинаковым именем, но несовместимый, который также использует libmagic
, Он также может получить кодировку, выполнив:
import magic
blob = open('unknown-file').read()
m = magic.Magic(mime_encoding=True)
encoding = m.from_buffer(blob)
Некоторые стратегии кодирования, пожалуйста, раскомментируйте по вкусу:
#!/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
Вот пример чтения и принятия за чистую монету 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']
Это может быть полезно
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'
Если вас не устраивают автоматические инструменты, вы можете попробовать все кодеки и вручную посмотреть, какой из них правильный.
all_codecs = ['ascii', 'big5', 'big5hkscs', 'cp037', 'cp273', 'cp424', 'cp437',
'cp500', 'cp720', 'cp737', 'cp775', 'cp850', 'cp852', 'cp855', 'cp856', 'cp857',
'cp858', 'cp860', 'cp861', 'cp862', 'cp863', 'cp864', 'cp865', 'cp866', 'cp869',
'cp874', 'cp875', 'cp932', 'cp949', 'cp950', 'cp1006', 'cp1026', 'cp1125',
'cp1140', 'cp1250', 'cp1251', 'cp1252', 'cp1253', 'cp1254', 'cp1255', 'cp1256',
'cp1257', 'cp1258', 'euc_jp', 'euc_jis_2004', 'euc_jisx0213', 'euc_kr',
'gb2312', 'gbk', 'gb18030', 'hz', 'iso2022_jp', 'iso2022_jp_1', 'iso2022_jp_2',
'iso2022_jp_2004', 'iso2022_jp_3', 'iso2022_jp_ext', 'iso2022_kr', 'latin_1',
'iso8859_2', 'iso8859_3', 'iso8859_4', 'iso8859_5', 'iso8859_6', 'iso8859_7',
'iso8859_8', 'iso8859_9', 'iso8859_10', 'iso8859_11', 'iso8859_13',
'iso8859_14', 'iso8859_15', 'iso8859_16', 'johab', 'koi8_r', 'koi8_t', 'koi8_u',
'kz1048', 'mac_cyrillic', 'mac_greek', 'mac_iceland', 'mac_latin2', 'mac_roman',
'mac_turkish', 'ptcp154', 'shift_jis', 'shift_jis_2004', 'shift_jisx0213',
'utf_32', 'utf_32_be', 'utf_32_le', 'utf_16', 'utf_16_be', 'utf_16_le', 'utf_7',
'utf_8', 'utf_8_sig']
def find_codec(text):
for i in all_codecs:
for j in all_codecs:
try:
print(i, "to", j, text.encode(i).decode(j))
except:
pass
find_codec("The example string which includes ö, ü, or ÄŸ, ö")
Этот сценарий создает не менее 9409 строк вывода. Итак, если вывод не помещается на экране терминала, попробуйте записать вывод в текстовый файл.
# 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()
В принципе, невозможно определить кодировку текстового файла в общем случае. Так что нет, для этого нет стандартной библиотеки Python.
Если у вас есть более конкретные знания о текстовом файле (например, о том, что это XML), возможно, существуют библиотечные функции.
В зависимости от вашей платформы, я просто выберу оболочку 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 для распознавания 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
Если вам известно какое-то содержимое файла, вы можете попытаться расшифровать его с помощью нескольких кодировок и посмотреть, чего не хватает. В общем, нет никакого способа, так как текстовый файл является текстовым файлом, и это глупо;)
Вы можете использовать пакет `python-magic, который не загружает весь файл в память:
import magic
def detect(
file_path,
):
return magic.Magic(
mime_encoding=True,
).from_file(file_path)
На выходе получается имя кодировки, например:
- iso-8859-1
- us-ascii
- utf-8
Вы можете использовать модуль chardet
import chardet
with open (filepath , "rb") as f:
data= f.read()
encode=chardet.UniversalDetector()
encode.close()
print(encode.result)
Или вы можете использовать команду chardet3 в Linux, но это займет некоторое время:
chardet3 fileName
Пример :
chardet3 donnee/dir/donnee.csv
donnee/dir/donnee.csv: ISO-8859-1 with confidence 0.73
Некоторые текстовые файлы знают о своей кодировке, большинство — нет. Осведомленный:
- текстовый файл со спецификацией
- XML-файл закодирован в UTF-8 или его кодировка указана в преамбуле
- файл JSON всегда кодируется в UTF-8
Не знают:
- CSV-файл
- любой случайный текстовый файл
Некоторые кодировки универсальны, т.е. они могут декодировать любую последовательность байтов, некоторые нет. US-ASCII не является универсальным, поскольку любой байт больше 127 не отображается ни на один символ. UTF-8 не универсален, поскольку любая последовательность байтов недействительна.
Напротив, Latin-1, Windows-1252 и т. д. универсальны (даже если некоторые байты официально не сопоставлены с символом):
>>> [b.to_bytes(1, 'big').decode("latin-1") for b in range(256)]
['\x00', ..., 'ÿ']
Учитывая случайный текстовый файл, закодированный в последовательности байтов, вы не можете определить кодировку, если файл не знает свою кодировку, потому что некоторые кодировки универсальны. Но иногда вы можете исключить неуниверсальные кодировки. Все универсальные кодировки по-прежнему возможны. В
chardet
модули используют частоту байтов, чтобы угадать, какая кодировка лучше всего подходит для закодированного текста.
Если вы не хотите использовать этот или аналогичный модуль, вот простой способ:
- проверить, знает ли файл свою кодировку (BOM)
- проверьте неуниверсальные кодировки и примите первую, которая может декодировать байты (ASCII перед UTF-8, потому что она строже)
- выбрать резервную кодировку.
Второй шаг немного рискован, если вы проверяете только образец, потому что некоторые байты в остальной части файла могут быть недействительными.
Код:
def guess_encoding(data: bytes, fallback: str = "iso8859_15") -> str:
"""
A basic encoding detector.
"""
for bom, encoding in [
(codecs.BOM_UTF32_BE, "utf_32_be"),
(codecs.BOM_UTF32_LE, "utf_32_le"),
(codecs.BOM_UTF16_BE, "utf_16_be"),
(codecs.BOM_UTF16_LE, "utf_16_le"),
(codecs.BOM_UTF8, "utf_8_sig"),
]:
if data.startswith(bom):
return encoding
if all(b < 128 for b in data):
return "ascii" # you may want to use the fallback here if data is only a sample.
decoder = codecs.getincrementaldecoder("utf_8")()
try:
decoder.decode(data, final=False)
except UnicodeDecodeError:
return fallback
else:
return "utf_8" # not certain if data is only a sample
Помните, что неуниверсальное кодирование может дать сбой. В
errors
параметр
decode
можно установить метод
'ignore'
,
'replace'
или же
'backslashreplace'
чтобы избежать исключений.
Давным-давно у меня была такая потребность.
Читая мой старый код, я нашел это:
import urllib.request
import chardet
import os
import settings
[...]
file = 'sources/dl/file.csv'
media_folder = settings.MEDIA_ROOT
file = os.path.join(media_folder, str(file))
if os.path.isfile(file):
file_2_test = urllib.request.urlopen('file://' + file).read()
encoding = (chardet.detect(file_2_test))['encoding']
return encoding
Это сработало для меня и вернулосьascii
Использование linux
file -i
команда
import subprocess
file = "path/to/file/file.txt"
encoding = subprocess.Popen("file -bi "+file, shell=True, stdout=subprocess.PIPE).stdout
encoding = re.sub(r"(\\n)[^a-z0-9\-]", "", str(encoding.read()).split("=")[1], flags=re.IGNORECASE)
print(encoding)