Як визначити кодування тексту?


219

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

Відповіді:


225

Правильно виявити кодування весь час неможливо .

(З FAQ Шардета :)

Однак деякі кодування оптимізовані для конкретних мов, а мови не є випадковими. Деякі послідовності символів спливають весь час, тоді як інші послідовності не мають сенсу. Людина, яка вільно розмовляє англійською мовою, яка відкриває газету і знаходить “txzqJv 2! Dasd0a QqdKjvz”, миттєво визнає, що це не англійська мова (навіть якщо вона складається повністю з англійських букв). Вивчаючи багато «типового» тексту, комп’ютерний алгоритм може імітувати подібний рівень вільності та робити зрозумілі здогадки про мову тексту.

Існує бібліотека chardet, яка використовує це дослідження, щоб спробувати виявити кодування. chardet - порт коду автоматичного виявлення в Mozilla.

Ви також можете використовувати UnicodeDammit . Буде спробувати наступні методи:

  • Кодування, виявлене в самому документі: наприклад, у декларації XML або (для документів HTML) тег META http-equiv. Якщо Beautiful Soup виявить такий тип кодування в документі, він знову аналізує документ із самого початку і намагається спробувати нове кодування. Єдиний виняток - якщо ви чітко вказали кодування, і це кодування фактично спрацювало: тоді воно буде ігнорувати будь-яке кодування, яке знайде в документі.
  • Кодування нюхає, дивлячись на перші кілька байтів файлу. Якщо кодування буде виявлено на цій стадії, це буде одне з кодувань UTF- *, EBCDIC або ASCII.
  • Кодування, нюхане бібліотекою шардетів , якщо у вас його встановлено.
  • UTF-8
  • Windows-1252

1
Дякую за chardetдовідку. Здається добре, хоча трохи повільно.
Крейг МакКуїн

17
@Geomorillo: Не існує такого поняття, як "стандарт кодування". Кодування тексту настільки ж стара, як і обчислення, воно органічно зростало з часом і потребами, воно не планувалося. "Unicode" - це спроба виправити це.
nosklo

1
І не поганий, всі речі враховані. Що я хотів би знати, як дізнатися, з чого було відкрито кодування відкритого текстового файлу?
holdenweb

2
@dumbledad, про що я говорив, це те, що правильно виявити його весь час неможливо. Все, що ви можете зробити, - це здогадка, але вона може часом виходити з ладу, вона не працюватиме кожен раз, оскільки кодування не справді виявляються. Для здогадки можна скористатись одним із інструментів, які я запропонував у відповіді
nosklo

1
@ LasseKärkkäinen суть цієї відповіді полягає в тому, щоб показати, що чітко виявити кодування неможливо ; функція, яку ви надаєте, може вгадати правильно для вашого випадку, але неправильна для багатьох випадків.
nosklo

67

Іншим варіантом розробки кодування є використання libmagic (що є кодом, що стоїть за командною файлом ). Існує велика кількість зв'язків пітона.

Прив'язки 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 тощо).
трійчатка

Шукаю способу управління таємницею кодування файлів, я знайшов цю публікацію. На жаль, використовуючи приклад коду, я не можу пройти повз open(): UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfc in position 169799: invalid start byte. Кодування файлу відповідно до vim's :set fileencodingє latin1.
xtian

Якщо я використовую необов'язковий аргумент errors='ignore', вихід з прикладу коду є менш корисним binary.
xtian

2
@xtian Вам потрібно відкрити у двійковому режимі, тобто відкрити ("filename.txt", "rb").
L. Kärkkäinen

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". Тож це, безумовно, є актуальним.
n158

1
це дуже добре для обробки невеликих наборів даних у різноманітних форматах. Тестував це рекурсивно на моєму кореневому режимі, і це працювало як частування. Дякую друже.
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

Залежно від вашої платформи, я просто вирішую використовувати команду linux shell 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 no bom: https://unicodebook.readthedocs.io/guess_encoding.html . Прочитайте файл у байтовому масиві (дані): http://www.codecodex.com/wiki/Read_a_file_into_a_byte_array . Ось приклад. Я в 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

Посилання на рішення вітається, але будь ласка, переконайтеся, що ваша відповідь корисна без неї: додайте контекст навколо посилання, щоб ваші колеги користувачі мали уявлення про те, що це таке і чому воно є, а потім наведіть найбільш релевантну частину сторінки, яку ви ' повторне посилання на випадок, якщо цільова сторінка недоступна. Відповіді, які є трохи більше ніж посилання, можуть бути видалені.
подвійний звуковий сигнал
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.