如何检测字符串的字节编码?

58

我已经使用os.listdir()读取了大约1000个文件名,其中一些是用UTF8编码的,而另一些则是CP1252编码。

我想将它们全部解码为Unicode,以便在我的脚本中进行进一步处理。有没有一种方法可以获取源编码以正确地解码为Unicode?

例如:

for item in os.listdir(rootPath):

    #Convert to Unicode
    if isinstance(item, str):
        item = item.decode('cp1252')  # or item = item.decode('utf-8')
    print item
6个回答

71

使用 chardet 库。它非常容易。

import chardet

the_encoding = chardet.detect('your string')['encoding']

就是这样!

在 Python 3 中,你需要提供类型为字节或字节数组的参数,因此:

import chardet
the_encoding = chardet.detect(b'your string')['encoding']

1
看起来好像不起作用。我创建了一个字符串变量并将其编码为 UTF-8。chardet 返回 TIS-620 编码。 - Taras Vaskiv
我发现cchardet似乎是这个或类似库的当前名称...; chardet无法找到。 - Martin Haeberli
对我而言,这个答案的问题在于一些cp1252 / latin1字符可以被解释为技术上有效的utf8 - 这导致ê类型字符出现在本应该是ê的地方。 chardet似乎首先尝试utf8,这就是结果。也许有一种方法可以告诉它使用哪个顺序,但lucemia的回答对我来说效果更好。 - artfulrobot
抱歉,我在上一条评论中描述时可能把utf8和cp1252搞反了! - artfulrobot
2
在Python 3中:TypeError: 期望字节或字节数组类型的对象,但得到了<class 'str'> - HelloGoodbye
显示剩余3条评论

38

如果你的文件使用的是 cp1252 或者 utf-8 编码,那么有一个简单的方法。

import logging
def force_decode(string, codecs=['utf8', 'cp1252']):
    for i in codecs:
        try:
            return string.decode(i)
        except UnicodeDecodeError:
            pass

    logging.warn("cannot decode url %s" % ([string]))

for item in os.listdir(rootPath):
    #Convert to Unicode
    if isinstance(item, str):
        item = force_decode(item)
    print item

否则,有一个字符集检测库。

Python - detect charset and convert to utf-8

https://pypi.python.org/pypi/chardet


14

你也可以使用json包来检测编码。

import json

json.detect_encoding(b"Hello")

1

charset_normalizerchardet 的一种替代品。

它对自然语言处理更加适用,且采用了宽松的 MIT 许可证:https://github.com/Ousret/charset_normalizer/

from charset_normalizer import detect
encoding = detect(byte_string)['encoding']

PS:这并不严格与原问题相关,但这个页面在谷歌上出现了很多次。


0

我尝试了使用json和chardet,得到了以下结果:

import json
import chardet

data = b'\xa9 2023'
json.detect_encoding(data)  # 'utf-8'
data.decode('utf-8')  # UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa9 in position 0: invalid start byte

chardet.detect(data)  # {'encoding': 'ISO-8859-1', 'confidence': 0.73, 'language': ''}
data.decode("ISO-8859-1")  # '© 2023'

0

chardet 检测到的编码可以用于解码字节数组而不会出现任何异常,但输出的字符串可能不正确。

对于已知编码,try ... except ... 的方式运行得非常完美,但并不适用于所有情况。

我们可以先使用 try ... except ...,然后再使用 chardet 作为备选方案:

    def decode(byte_array: bytearray, preferred_encodings: List[str] = None):
        if preferred_encodings is None:
            preferred_encodings = [
                'utf8',       # Works for most cases
                'cp1252'      # Other encodings may appear in your project
            ]

        for encoding in preferred_encodings:
            # Try preferred encodings first
            try:
                return byte_array.decode(encoding)
            except UnicodeDecodeError:
                pass
        else:
            # Use detected encoding
            encoding = chardet.detect(byte_array)['encoding']
            return byte_array.decode(encoding)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接