使用Python检测文本文件编码时可能会遇到的陷阱?

5
我对自行车维修、电锯使用和沟渠安全的了解比对Python或文本编码的了解更多。鉴于此,Python文本编码似乎是一个长期存在的问题(我的问题:在Python中搜索各种编码的文本文件内容?和其他我读过的问题:12)。我尝试编写一些代码来猜测编码。

在有限的测试中,这段代码似乎可以在不需要我过多了解文本编码的前三个字节以及那些数据不提供信息的情况下达到我的目的*。

*我的目的是:

  1. 拥有一个无需依赖的片段,我可以用它来实现中高度成功率,
  2. 扫描本地工作站上的任何编码的基于文本的日志文件,并根据其内容将其识别为我感兴趣的文件(这需要使用正确的编码打开文件)
  3. 挑战自己,让它能够正常工作。

问题:使用我下面所做的比较和计数字符的笨拙方法有什么缺陷?非常感谢您提供任何意见。

def guess_encoding_debug(file_path):
    """
    DEBUG - returns many 2 value tuples
    Will return list of all possible text encodings with a count of the number of chars
    read that are common characters, which might be a symptom of success.
    SEE warnings in sister function
    """

    import codecs
    import string
    from operator import itemgetter

    READ_LEN = 1000
    ENCODINGS = ['ascii','cp1252','mac_roman','utf_8','utf_16','utf_16_le',\
                 'utf_16_be','utf_32','utf_32_le','utf_32_be']

    #chars in the regular ascii printable set are BY FAR the most common
    #in most files written in English, so their presence suggests the file
    #was decoded correctly.
    nonsuspect_chars = string.printable

    #to be a list of 2 value tuples
    results = []

    for e in ENCODINGS:
        #some encodings will cause an exception with an incompatible file,
        #they are invalid encoding, so use try to exclude them from results[]
        try:
            with codecs.open(file_path, 'r', e) as f:

                #sample from the beginning of the file
                data = f.read(READ_LEN)

                nonsuspect_sum = 0

                #count the number of printable ascii chars in the
                #READ_LEN sized sample of the file
                for n in nonsuspect_chars:
                    nonsuspect_sum += data.count(n)

                #if there are more chars than READ_LEN
                #the encoding is wrong and bloating the data
                if nonsuspect_sum <= READ_LEN:
                    results.append([e, nonsuspect_sum])
        except:
            pass

    #sort results descending based on nonsuspect_sum portion of
    #tuple (itemgetter index 1).
    results = sorted(results, key=itemgetter(1), reverse=True)

    return results


def guess_encoding(file_path):
    """
    Stupid, simple, slow, brute and yet slightly accurate text file encoding guessing.
    Will return one likely text encoding, though there may be others just as likely.
    WARNING: DO NOT use if your file uses any significant number of characters
             outside the standard ASCII printable characters!
    WARNING: DO NOT use for critical applications, this code will fail you.
    """

    results = guess_encoding_debug(file_path)

    #return the encoding string (second 0 index) from the first
    #result in descending list of encodings (first 0 index)
    return results[0][0]

我认为与我不太熟悉的chardet相比,它可能会慢一些,并且不够准确。它的设计方式导致任何基于罗马字符的带有重音、分音符等语言都不能很好地工作。这就使得当它失效时很难知道。然而,大部分英文文本,包括大多数编程代码,都是使用此代码所依赖的string.printable来编写的。
未来,使用外部库可能是一个选择,但目前我想避免这样做,因为:
  1. 这个脚本将在公司内外的多台计算机上运行,这些计算机使用不同版本的Python,因此越简单越好。当我说“公司”时,我指的是由社会科学家组成的小型非营利组织。
  2. 我负责收集GPS数据处理日志,但我不是系统管理员,她不是Python程序员,我占用她的时间越少越好。
  3. 我的公司通常安装Python的版本是与GIS软件包一起安装的,如果让它保持原样通常会更好。
  4. 我的需求并不是特别严格,我只想识别出我感兴趣的文件,并使用其他方法将它们复制到归档中。我并不是将整个内容读入内存进行操作,也不是将其附加或重写。
  5. 高级编程语言应该有一些自己的方法来完成这项任务。虽然“似乎”是任何事业的不稳定基础,但我想尝试并看看是否可以让它工作。

写得好的问题加一!这是一个经过充分研究和精心撰写的例子。 - Jeff Tratner
1
有没有不尝试现有库的原因?比如 chardet 或 chared(https://code.google.com/p/chared/)? - amit
据我所知,有一些非常好的库可用于这种排序,例如您提到的那些。我希望有时间学习如何使用它们,因为我相信它们比我能想出的更强大。然而,避免使用外部库有很多原因。我已经编辑了我的帖子以更好地阐述我的理由。 - Matt Bell
如果所有需要运行的系统都是*nix系统(即非Windows),你可以作弊。所谓“作弊”,就是使用os.system调用“file $x”来确认文件类型。 - Ben
1个回答

0

了解代码的运行情况最简单的方法可能是使用其他现有库的测试套件,并将其作为创建自己全面测试套件的基础。这样,您就可以知道您的代码是否适用于所有这些情况,并且还可以测试您关心的所有情况。


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