Làm thế nào để xác định mã hóa văn bản?


Câu trả lời:


225

Phát hiện chính xác mã hóa mọi lúc là không thể .

(Từ Câu hỏi thường gặp về chardet :)

Tuy nhiên, một số mã hóa được tối ưu hóa cho các ngôn ngữ cụ thể và ngôn ngữ không phải là ngẫu nhiên. Một số chuỗi nhân vật bật lên mọi lúc, trong khi các chuỗi khác không có ý nghĩa. Một người thông thạo tiếng Anh, người mở một tờ báo và tìm thấy TxzqJv 2! Bằng cách nghiên cứu nhiều văn bản điển hình của người Viking, một thuật toán máy tính có thể mô phỏng loại lưu loát này và đưa ra phỏng đoán có giáo dục về ngôn ngữ của văn bản.

Có thư viện chardet sử dụng nghiên cứu đó để cố gắng phát hiện mã hóa. chardet là một cổng của mã phát hiện tự động trong Mozilla.

Bạn cũng có thể sử dụng UnicodeDammit . Nó sẽ thử các phương pháp sau:

  • Một mã hóa được phát hiện trong chính tài liệu: ví dụ, trong một khai báo XML hoặc (đối với các tài liệu HTML) một thẻ META http-Equiv. Nếu Beautiful Soup tìm thấy loại mã hóa này trong tài liệu, nó sẽ phân tích lại tài liệu ngay từ đầu và thử mã hóa mới. Ngoại lệ duy nhất là nếu bạn chỉ định rõ ràng một mã hóa và mã hóa đó thực sự hoạt động: thì nó sẽ bỏ qua mọi mã hóa mà nó tìm thấy trong tài liệu.
  • Một mã hóa đánh hơi bằng cách nhìn vào vài byte đầu tiên của tệp. Nếu mã hóa được phát hiện ở giai đoạn này, nó sẽ là một trong những mã hóa UTF- *, EBCDIC hoặc ASCII.
  • Một mã hóa được đánh hơi bởi thư viện chardet , nếu bạn đã cài đặt nó.
  • UTF-8
  • Windows-1252

1
Cảm ơn đã chardettham khảo. Có vẻ tốt, mặc dù hơi chậm.
Craig McQueen

17
@Geomorillo: Không có thứ gọi là "tiêu chuẩn mã hóa". Mã hóa văn bản là một cái gì đó lâu đời như điện toán, nó phát triển hữu cơ theo thời gian và nhu cầu, nó không được lên kế hoạch. "Unicode" là một nỗ lực để khắc phục điều này.
nosklo

1
Và không phải là một xấu, tất cả mọi thứ xem xét. Những gì tôi muốn biết là, làm thế nào để tôi tìm ra mã hóa một tệp văn bản mở được mở bằng gì?
Holdenweb

2
@dumbledad điều tôi nói là phát hiện chính xác mọi lúc là không thể. Tất cả những gì bạn có thể làm là đoán, nhưng đôi khi nó có thể thất bại, nó sẽ không hoạt động mọi lúc, do mã hóa không thực sự được phát hiện. Để đoán, bạn có thể sử dụng một trong những công cụ tôi đề xuất trong câu trả lời
nosklo

1
@ LasseKärkkäinen quan điểm của câu trả lời đó là chỉ ra rằng việc phát hiện mã hóa thực sự là không thể ; chức năng bạn cung cấp có thể đoán đúng cho trường hợp của bạn, nhưng sai cho nhiều trường hợp.
nosklo

67

Một tùy chọn khác để thực hiện mã hóa là sử dụng libmagic (là mã đằng sau lệnh tệp ). Có một sự ràng buộc của ràng buộc trăn có sẵn.

Các ràng buộc python sống trong cây nguồn tệp có sẵn dưới dạng gói debian python-magic (hoặc python3-magic ). Nó có thể xác định mã hóa của một tệp bằng cách thực hiện:

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

Có một gói pip python-Magic giống hệt nhau, nhưng không tương thích trên pypi cũng sử dụng libmagic. Nó cũng có thể nhận được mã hóa, bằng cách thực hiện:

import magic

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

5
libmagicthực sự là một lựa chọn khả thi để chardet. Và thông tin tuyệt vời về các gói khác nhau được đặt tên python-magic! Tôi chắc rằng sự mơ hồ này đã cắn nhiều người
MestreLion

1
filekhông đặc biệt tốt trong việc xác định ngôn ngữ của con người trong các tệp văn bản. Thật tuyệt vời khi xác định các định dạng chứa khác nhau, mặc dù đôi khi bạn phải biết ý nghĩa của nó ("tài liệu Microsoft Office" có thể có nghĩa là một thông báo Outlook, v.v.).
tripleee

Tìm kiếm một cách để quản lý bí mật mã hóa tập tin, tôi tìm thấy bài viết này. Thật không may, bằng cách sử dụng mã ví dụ, tôi không thể vượt qua open(): UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfc in position 169799: invalid start byte. Mã hóa tập tin theo vim :set fileencodinglatin1.
xtian

Nếu tôi sử dụng đối số tùy chọn errors='ignore', đầu ra của mã ví dụ sẽ ít hữu ích hơn binary.
xtian

2
@xtian Bạn cần mở ở chế độ nhị phân, tức là mở ("filename.txt", "rb").
L. Kärkkäinen

31

Một số chiến lược mã hóa, xin vui lòng bỏ ghi chú:

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

Bạn có thể muốn kiểm tra mã hóa bằng cách mở và đọc tệp dưới dạng vòng lặp ... nhưng trước tiên bạn có thể cần kiểm tra kích thước tệp:

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              

Bạn cũng có thể sử dụng io, như io.open(filepath, 'r', encoding='utf-8'), thuận tiện hơn, vì codecskhông \ntự động chuyển đổi khi đọc và viết. Thêm vào ĐÂY
Searene

23

Dưới đây là một ví dụ về đọc và lấy theo mệnh giá chardetdự đoán mã hóa, đọc n_linestừ tệp trong trường hợp nó lớn.

chardetcũng cung cấp cho bạn một xác suất (nghĩa là confidence) dự đoán mã hóa của nó (chưa xem cách họ đưa ra điều đó), được trả về với dự đoán của nó từ đó chardet.predict(), vì vậy bạn có thể làm việc theo cách nào đó nếu bạn muốn.

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

Nhìn vào điều này sau khi nhận được một phiếu bầu và bây giờ thấy rằng giải pháp này có thể chậm lại nếu có nhiều dữ liệu trên dòng đầu tiên. Trong một số trường hợp, tốt hơn là đọc dữ liệu theo cách khác.
ryanjdillon

2
Tôi đã sửa đổi chức năng này theo cách này: def predict_encoding(file_path, n=20): ... skip ... and then rawdata = b''.join([f.read() for _ in range(n)]) Đã thử chức năng này trên Python 3.6, hoạt động hoàn hảo với các mã hóa "ascii", "cp1252", "utf-8", "unicode". Vì vậy, đây chắc chắn là upvote.
n158

1
điều này rất tốt để xử lý các bộ dữ liệu nhỏ với nhiều định dạng khác nhau. Đã thử nghiệm đệ quy này trên thư mục gốc của tôi và nó hoạt động như một điều trị. Cảm ơn cậu.
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

Tùy thuộc vào nền tảng của bạn, tôi chỉ chọn sử dụng filelệnh shell linux . Điều này làm việc cho tôi vì tôi đang sử dụng nó trong một tập lệnh chỉ chạy trên một trong các máy linux của chúng tôi.

Rõ ràng đây không phải là một giải pháp hay câu trả lời lý tưởng, nhưng nó có thể được sửa đổi để phù hợp với nhu cầu của bạn. Trong trường hợp của tôi, tôi chỉ cần xác định xem một tệp có phải là UTF-8 hay không.

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

Ngã ba một quá trình mới là không cần thiết. Mã Python đã chạy bên trong một quy trình và có thể tự gọi các chức năng hệ thống phù hợp mà không cần quá trình tải một quy trình mới.
vdboor

2

Điều này có thể hữu ích

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

Về nguyên tắc, về nguyên tắc, không thể xác định mã hóa của tệp văn bản, trong trường hợp chung. Vì vậy, không có thư viện Python chuẩn để làm điều đó cho bạn.

Nếu bạn có kiến ​​thức cụ thể hơn về tệp văn bản (ví dụ: đó là XML), có thể có các hàm thư viện.


1

Nếu bạn biết một số nội dung của tệp, bạn có thể thử giải mã nó bằng một vài mã hóa và xem cái nào còn thiếu. Nói chung, không có cách nào vì tệp văn bản là tệp văn bản và những tệp đó là ngu ngốc;)


1

Trang web này có mã python để nhận dạng ascii, mã hóa bằng boms và utf8 no bom: https://unicodebook.readthedocs.io/guess_encoding.html . Đọc tệp thành mảng byte (dữ liệu): http://www.codecodex.com/wiki/Read_a_file_into_a_byte_array . Đây là một ví dụ. Tôi đang ở 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

Liên kết đến một giải pháp được hoan nghênh, nhưng vui lòng đảm bảo câu trả lời của bạn hữu ích mà không cần đến nó: thêm ngữ cảnh xung quanh liên kết để người dùng của bạn sẽ có ý tưởng về nó là gì và tại sao lại có, sau đó trích dẫn phần có liên quan nhất của trang bạn ' liên kết lại trong trường hợp trang đích không có sẵn. Câu trả lời ít hơn một liên kết có thể bị xóa.
tiếng bíp đôi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.