如何确定文本的编码?

Posted

技术标签:

【中文标题】如何确定文本的编码?【英文标题】:How to determine the encoding of text? 【发布时间】:2010-09-30 23:25:30 【问题描述】:

我收到了一些经过编码的文本,但我不知道使用了什么字符集。有没有办法使用 Python 确定文本文件的编码? How can I detect the encoding/codepage of a text file 处理 C#。

【问题讨论】:

【参考方案1】:

在一般情况下,原则上不可能确定文本文件的编码。所以不,没有标准的 Python 库可以为您做到这一点。

如果您对文本文件有更具体的了解(例如它是 XML),则可能有库函数。

【讨论】:

【参考方案2】:

如果您知道文件的某些内容,您可以尝试使用多种编码对其进行解码,然后查看丢失的内容。一般来说,没有办法,因为文本文件是文本文件,而且那些很愚蠢;)

【讨论】:

【参考方案3】:

编辑:chardet 似乎无人维护,但大多数答案都适用。检查https://pypi.org/project/charset-normalizer/ 以获取替代方法

始终正确检测编码不可能

(来自 chardet 常见问题解答:)

不过,有些编码已经过优化 对于特定的语言和语言 不是随机的。一些性格 序列一直弹出,而 其他序列没有意义。一种 英语流利的人打开一个 报纸并找到“txzqJv 2!dasd0a QqdKjvz”会立即识别 那不是英语(即使它是 完全由英文字母组成)。 通过研究大量“典型”文本, 计算机算法可以模拟这个 一种流利,使一个受过教育的人 猜测文本的语言。

chardet 库使用该研究来尝试检测编码。 chardet 是 Mozilla 中自动检测代码的一个端口。

您也可以使用UnicodeDammit。它会尝试以下方法:

在文档本身中发现的编码:例如,在 XML 声明或(对于 html 文档)http-equiv META 标记中。如果 Beautiful Soup 在文档中发现这种编码,它会重新从头开始解析文档并尝试新的编码。唯一的例外是,如果您明确指定了编码,并且该编码确实有效:那么它将忽略它在文档中找到的任何编码。 通过查看文件的前几个字节来嗅探的编码。如果在此阶段检测到编码,它将是 UTF-* 编码、EBCDIC 或 ASCII 之一。 chardet 库嗅探到的编码(如果已安装)。 UTF-8 Windows-1252

【讨论】:

感谢chardet 参考。看起来不错,虽然有点慢。 @Geomorillo:没有所谓的“编码标准”。文本编码与计算一样古老,它随着时间和需求而有机地发展,它没有计划。 “Unicode”试图解决这个问题。 而且还不错,考虑到所有因素。我想知道的是,如何找出打开的文本文件是用什么编码打开的? @dumbledad 我所说的是,始终正确检测它是不可能的。您所能做的只是猜测,但有时它可能会失败,它不会每次都有效,因为编码不是真正可检测到的。要进行猜测,您可以使用我在答案中建议的工具之一 显然cchardet 更快,但需要cython【参考方案4】:

一些编码策略,请取消注释品尝:

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

您可能希望通过以循环的形式打开和读取文件来检查编码...但您可能需要先检查文件大小:

# PYTHON
encodings = ['utf-8', 'windows-1250', 'windows-1252'] # add more
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。更多关于HERE【参考方案5】:

计算编码的另一个选项是使用 libmagic(这是后面的代码 file 命令)。有丰富的 可用的 python 绑定。

文件源树中的 python 绑定可用作 python-magic(或python3-magic) Debian 软件包。它可以通过执行以下操作来确定文件的编码:

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

pypi 上有一个同名但不兼容的python-magic pip 包,它也使用libmagic。它还可以通过以下方式获取编码:

import magic

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

【讨论】:

libmagic 确实是chardet 的可行替代方案。以及有关名为 python-magic 的不同软件包的重要信息!我敢肯定,这种模棱两可的问题困扰着很多人 file 不是特别擅长识别文本文件中的人类语言。它非常适合识别各种容器格式,尽管您有时必须知道它的含义(“Microsoft Office 文档”可能意味着 Outlook 消息等)。 寻找一种管理文件编码之谜的方法我发现了这篇文章。不幸的是,使用示例代码,我无法通过open():UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfc in position 169799: invalid start byte。根据vim的:set fileencoding,文件编码为latin1 如果我使用可选参数errors='ignore',示例代码的输出是不太有用的binary @xtian 需要以二进制方式打开,即 open("filename.txt", "rb")。【参考方案6】:
# 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()

【讨论】:

【参考方案7】:

根据您的平台,我只选择使用 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 代码已经在进程中运行,并且可以自己调用适当的系统函数,而无需加载新进程的开销。【参考方案8】:

这是一个读取和获取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']

【讨论】:

在获得支持后查看此内容,现在发现如果第一行有大量数据,此解决方案可能会变慢。在某些情况下,以不同的方式读取数据会更好。 我已经这样修改了这个函数: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,是否有可能停止读取字符中间的字节并混淆chardet 在所有很棒的建议中,我发现这是最有用的。我还包括@n158 建议只读取 20 个字节而不是 20 行。【参考方案9】:

此站点有用于识别 ascii、使用 bom 编码和 utf8 无 bom 的 python 代码: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

【讨论】:

欢迎提供解决方案的链接,但请确保您的答案在没有它的情况下有用:add context around the link 这样您的其他用户就会知道它是什么以及为什么会出现,然后引用最相关的内容您链接到的页面的一部分,以防目标页面不可用。 Answers that are little more than a link may be deleted.【参考方案10】:

这可能会有所帮助

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'

【讨论】:

【参考方案11】:

使用linuxfile -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)

【讨论】:

【参考方案12】:

你可以使用`python-magic package,它不会将整个文件加载到内存中:

import magic


def detect(
    file_path,
):
    return magic.Magic(
        mime_encoding=True,
    ).from_file(file_path)

输出是编码名称,例如:

iso-8859-1 us-ascii utf-8

【讨论】:

【参考方案13】:

您可以使用 chardet 模块

import chardet

with open (filepath , "rb") as f:
    data= f.read()
    encode=chardet.UniversalDetector()
    encode.close()
    print(encode.result)

或者你可以在 linux 中使用 chardet3 命令,但这需要一些时间:

chardet3 fileName

例子:

chardet3 donnee/dir/donnee.csv
donnee/dir/donnee.csv: ISO-8859-1 with confidence 0.73

【讨论】:

【参考方案14】:

如果您对自动工具不满意,您可以尝试所有编解码器并手动查看哪个编解码器是正确的。

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 行输出。因此,如果输出不适合终端屏幕,请尝试将输出写入文本文件。

【讨论】:

以上是关于如何确定文本的编码?的主要内容,如果未能解决你的问题,请参考以下文章

如何快速确定给定邮政编码的状态?

如何确定文本文件的编码表

widows如何批量修改文件的编码格式

确定文本文件是否为Unicode编码

确定 TextFile 编码?

Linux 文本文件编码转换工具