将一堆文件从猜测编码转换为UTF-8

时间:2015-02-06 08:52:46

标签: python encoding utf-8 character-encoding

我有this Python script尝试检测文本文件的字符编码(在本例中为C#.cs源文件,但它们可以是任何文本文件)然后从该字符转换它们编码并进入UTF-8(无BOM)。

虽然chardet能够很好地检测到编码并且脚本正常运行,但©等字符会被编码为$。所以我假设脚本和我对Python 2中编码的理解有问题。因为将文件从UTF-8-SIG转换为UTF-8有效,我感觉问题是解码(阅读)部分和不是编码(写作)部分。

谁能告诉我我做错了什么?如果切换到Python 3是一个解决方案,我就是全力以赴,然后我需要帮助弄清楚如何将脚本从2.7版本运行转换为3.4版本。这是脚本:

import os
import glob
import fnmatch
import codecs
from chardet.universaldetector import UniversalDetector

# from http://farmdev.com/talks/unicode/
def to_unicode_or_bust(obj, encoding='utf-8'):
    if isinstance(obj, basestring):
        if not isinstance(obj, unicode):
            obj = unicode(obj, encoding)
    return obj

def enforce_unicode():
    detector = UniversalDetector()

    for root, dirnames, filenames in os.walk('.'):
      for filename in fnmatch.filter(filenames, '*.cs'):
        detector.reset()
        filepath = os.path.join(root, filename)

        with open(filepath, 'r') as f:
            for line in f:
                detector.feed(line)
                if detector.done: break

        detector.close()
        encoding = detector.result['encoding']

        if encoding and not encoding == 'UTF-8':
            print '%s -> UTF-8   %s' % (encoding.ljust(12), filepath)
            with codecs.open(filepath, 'r', encoding=encoding) as f:
                content = ''.join(f.readlines())

            content = to_unicode_or_bust(content)

            with codecs.open(filepath, 'w', encoding='utf-8') as f:
                f.write(content)

enforce_unicode()

我在编写文件之前尝试过content = content.decode(encoding).encode('utf-8')但是失败并出现以下错误:

/usr/local/.../lib/python2.7/encodings/utf_8_sig.py:19: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
  if input[:3] == codecs.BOM_UTF8:
Traceback (most recent call last):
  File "./enforce-unicode.py", line 48, in <module>
    enforce_unicode()
  File "./enforce-unicode.py", line 43, in enforce_unicode
    content = content.decode(encoding).encode('utf-8')
  File "/usr/local/.../lib/python2.7/encodings/utf_8_sig.py", line 22, in decode
    (output, consumed) = codecs.utf_8_decode(input, errors, True)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa9' in position 87: ordinal not in range(128)

想法?

1 个答案:

答案 0 :(得分:2)

chardet只是让检测到的编解码器错误,否则您的代码是正确的。字符检测基于统计,启发式和简单猜测,它不是一种万无一失的方法。

例如,Windows 1252 codepage与Latin-1编解码器非常接近;用一种编码编码的文件可以在其他编码中无错误地解码。检测一个中的控制代码或另一个中的欧元符号之间的差异通常需要人类查看结果。

我会记录每个文件的chardet猜测,如果文件被错误地重新编码,您需要查看其他编解码器可能关闭的内容。所有1250系列的代码页看起来都很相似。