จะตรวจสอบการเข้ารหัสของข้อความได้อย่างไร?


219

ฉันได้รับข้อความที่เข้ารหัส แต่ฉันไม่รู้ว่าใช้ชุดอักขระอะไร มีวิธีในการกำหนดการเข้ารหัสไฟล์ข้อความด้วย Python หรือไม่? ฉันจะตรวจจับการเข้ารหัส / เพจรหัสของไฟล์ข้อความที่เกี่ยวข้องกับ C # ได้อย่างไร

คำตอบ:


225

ได้อย่างถูกต้องตรวจสอบเวลาการเข้ารหัสทั้งหมดเป็นไปไม่ได้

(จากชาร์ตคำถามที่พบบ่อย :)

อย่างไรก็ตามการเข้ารหัสบางอย่างได้รับการปรับให้เหมาะสมกับภาษาเฉพาะและภาษานั้นไม่ได้สุ่ม ลำดับตัวละครบางตัวปรากฏขึ้นตลอดเวลาในขณะที่ลำดับอื่น ๆ ไม่มีความหมาย บุคคลที่พูดภาษาอังกฤษได้อย่างคล่องแคล่วและเปิดหนังสือพิมพ์และพบว่า "txzqJv 2! dasd0a QqdKjvz" จะรับรู้ได้ทันทีว่าไม่ใช่ภาษาอังกฤษ (แม้ว่าจะประกอบด้วยตัวอักษรภาษาอังกฤษทั้งหมด) ด้วยการศึกษาข้อความ“ ทั่วไป” จำนวนมากอัลกอริธึมคอมพิวเตอร์สามารถจำลองความคล่องแคล่วเช่นนี้และคาดเดาเกี่ยวกับภาษาของข้อความ

มีไลบรารีchardetที่ใช้การศึกษานั้นเพื่อพยายามตรวจจับการเข้ารหัส chardet เป็นพอร์ตของรหัสตรวจจับอัตโนมัติใน Mozilla

นอกจากนี้คุณยังสามารถใช้UnicodeDammit มันจะลองวิธีการต่อไปนี้:

  • การเข้ารหัสที่ค้นพบในเอกสารนั้น: ตัวอย่างเช่นในการประกาศ XML หรือ (สำหรับเอกสาร HTML) แท็ก META ของ http-equiv หาก Beautiful Soup พบการเข้ารหัสชนิดนี้ภายในเอกสารมันจะทำการแยกวิเคราะห์เอกสารอีกครั้งตั้งแต่ต้นและให้ลองทำการเข้ารหัสใหม่ ข้อยกเว้นเพียงอย่างเดียวคือถ้าคุณระบุการเข้ารหัสอย่างชัดเจนและการเข้ารหัสนั้นใช้งานได้จริง: จากนั้นจะละเว้นการเข้ารหัสที่พบในเอกสาร
  • การเข้ารหัสดมโดยดูที่สองสามไบต์แรกของไฟล์ หากตรวจพบการเข้ารหัสในขั้นตอนนี้จะเป็นหนึ่งในการเข้ารหัส UTF- *, EBCDIC หรือ ASCII
  • การเข้ารหัสที่ดมกลิ่นโดยห้องสมุดchardetหากคุณติดตั้งแล้ว
  • UTF-8
  • 1252 หน้าต่าง

1
ขอบคุณสำหรับการchardetอ้างอิง ดูดีแม้ว่าจะช้าไปนิด
Craig McQueen

17
@Geomorillo: ไม่มีสิ่งเช่น "มาตรฐานการเข้ารหัส" การเข้ารหัสข้อความเป็นสิ่งที่เก่าแก่พอ ๆ กับการคำนวณมันเติบโตอย่างเป็นธรรมชาติตามเวลาและความต้องการไม่มีการวางแผน "Unicode" เป็นความพยายามในการแก้ไขปัญหานี้
nosklo

1
และไม่ใช่สิ่งที่ไม่ดีทุกสิ่งถือว่า สิ่งที่ฉันอยากรู้คือฉันจะค้นหาได้อย่างไรว่ามีการเข้ารหัสไฟล์ข้อความแบบเปิดที่เปิดอยู่ด้วยอย่างไร
holdenweb

2
@dumbledad สิ่งที่ฉันพูดคือการตรวจสอบอย่างถูกต้องตลอดเวลาเป็นไปไม่ได้ สิ่งที่คุณทำได้คือการเดา แต่มันอาจล้มเหลวบางครั้งมันจะไม่ทำงานทุกครั้งเนื่องจากการเข้ารหัสไม่สามารถตรวจจับได้ ในการคาดเดาคุณสามารถใช้หนึ่งในเครื่องมือที่ฉันแนะนำในคำตอบ
nosklo

1
@ LasseKärkkäinenจุดของคำตอบว่าคือการแสดงให้เห็นว่าการเข้ารหัส corectly การตรวจสอบเป็นไปไม่ได้ที่ ; ฟังก์ชั่นที่คุณให้สามารถคาดเดาได้ถูกต้องสำหรับกรณีของคุณ แต่ผิดสำหรับหลาย ๆ กรณี
nosklo

67

ตัวเลือกอื่นสำหรับการทำงานการเข้ารหัสคือการใช้ libmagic (ซึ่งเป็นรหัสหลัง คำสั่งไฟล์ ) มีความหลากหลายของการผูกหลามที่มีอยู่

การรวมงูหลามที่อาศัยอยู่ในต้นไม้ต้นกำเนิดไฟล์นั้นมีอยู่ในแพ็คเกจ 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

มีแพ็คเกจ pip python-magic ที่มีชื่อเหมือนกัน แต่เข้ากันไม่ได้บน pypi ที่ใช้libmagicเช่นกัน นอกจากนี้ยังสามารถรับการเข้ารหัสโดยทำ:

import magic

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

5
libmagicchardetย่อมเป็นทางเลือกที่ทำงานได้ และข้อมูลที่ยอดเยี่ยมเกี่ยวกับแพ็คเกจที่แตกต่างชื่อpython-magic! ฉันแน่ใจ
ว่าความ

1
fileไม่ค่อยดีนักในการระบุภาษามนุษย์ในไฟล์ข้อความ มันยอดเยี่ยมสำหรับการระบุรูปแบบคอนเทนเนอร์ต่าง ๆ แม้ว่าบางครั้งคุณต้องรู้ว่ามันหมายถึงอะไร ("เอกสาร Microsoft Office" อาจหมายถึงข้อความ Outlook และอื่น ๆ )
tripleee

กำลังมองหาวิธีจัดการความลึกลับในการเข้ารหัสไฟล์ฉันพบโพสต์นี้แล้ว น่าเสียดายที่ใช้รหัสตัวอย่างฉันไม่สามารถผ่านไปได้open(): UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfc in position 169799: invalid start byte. แฟ้มการเข้ารหัสตามเสียงเรียกของมี: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" encodings ดังนั้นนี่คือ upvote แน่นอน
n158

1
นี่เป็นสิ่งที่ดีมากสำหรับการจัดการชุดข้อมูลขนาดเล็กที่มีรูปแบบที่หลากหลาย ทดสอบสิ่งนี้ซ้ำ ๆ บน dir รูทของฉันและมันทำงานเหมือนการรักษา ขอบคุณเพื่อน.
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

ฉันแค่เลือกใช้fileคำสั่งlinux shell ขึ้นอยู่กับแพลตฟอร์มของคุณ สิ่งนี้ใช้ได้กับฉันตั้งแต่ฉันใช้มันในสคริปต์ที่ทำงานบนเครื่องลินุกซ์ของเรา

เห็นได้ชัดว่านี่ไม่ใช่ทางออกหรือคำตอบในอุดมคติ แต่สามารถปรับเปลี่ยนให้เหมาะกับความต้องการของคุณ ในกรณีของฉันฉันแค่ต้องตรวจสอบว่าไฟล์เป็น 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')

ไม่จำเป็นต้องทำการแยกกระบวนการใหม่ รหัสไพ ธ อนทำงานอยู่ภายในกระบวนการและสามารถเรียกใช้ฟังก์ชันของระบบที่เหมาะสมได้เองโดยไม่ต้องมีค่าใช้จ่ายในการโหลดกระบวนการใหม่
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

เว็บไซต์นี้มีรหัสหลามการตระหนักถึง ASCII, การเข้ารหัสด้วย boms และ utf8 ไม่มี 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

ยินดีต้อนรับลิงก์ไปยังโซลูชัน แต่โปรดมั่นใจว่าคำตอบของคุณมีประโยชน์หากไม่มี: เพิ่มบริบทรอบลิงก์เพื่อให้ผู้ใช้เพื่อนของคุณมีความคิดว่ามันคืออะไรและเพราะเหตุใดจึงอยู่ที่นั่น กำลังเชื่อมโยงไปยังในกรณีที่หน้าเป้าหมายไม่พร้อมใช้งาน คำตอบที่น้อยกว่าลิงก์อาจถูกลบ
เสียงบี๊บสองครั้ง
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.