【问题标题】:Wanted Python to create a UTF-8 File, got an ANSI one. Why?想要 Python 创建一个 UTF-8 文件,得到一个 ANSI 文件。为什么?
【发布时间】:2011-12-24 22:06:25
【问题描述】:

我有以下功能:

def storeTaggedCorpus(corpus, filename):
    corpusFile = codecs.open(filename, mode = 'w', encoding = 'utf-8')
    for token in corpus:
        tagged_token = '/'.join(str for str in token)
        tagged_token = tagged_token.decode('ISO-8859-1')
        tagged_token = tagged_token.encode('utf-8')
        corpusFile.write(tagged_token)
        corpusFile.write(u"\n")
    corpusFile.close()

当我执行它时,出现以下错误:

(...) in storeTaggedCorpus
    corpusFile.write(tagged_token)
  File "c:\Python26\lib\codecs.py", line 691, in write
    return self.writer.write(data)
  File "c:\Python26\lib\codecs.py", line 351, in write
    data, consumed = self.encode(object, self.errors)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)

所以我去调试它,发现创建的文件被编码为 ANSI,而不是corpusFile = codecs.open(filename, mode = 'w', encoding = 'utf-8') 中声明的 UTF-8。如果 corpusFile.write(tagged_token) 被删除,这个函数将(显然)工作,文件将被编码为 ANSI。相反,如果我删除tagged_token = tagged_token.encode('utf-8'),它也会起作用,但是生成的文件将具有编码“ANSI as UTF-8”(???)并且拉丁字符将被破坏。由于我正在分析 pt-br 文本,因此这是不可接受的。

我相信如果 corpusFile 以 UTF-8 格式打开,一切都会正常工作,但我无法让它工作。我在网上搜索过,但我发现的关于 Python/Unicode 的所有内容都涉及其他内容……那么为什么这个文件总是以 ANSI 结尾?我在 Windows 7 x64 中使用 Python 2.6,这些文件编码是从 Notepad++ 通知的。

编辑——关于corpus参数

我不知道corpus 字符串的编码。它是由PlaintextCorpusReader.tag() 方法从NLTK 生成的。根据 Notepad++,原始语料库文件以 UTF-8 编码。 tagged_token.decode('ISO-8859-1') 只是一个猜测。我尝试将其解码为 cp1252,并从 ISO-8859-1 中获得了相同的损坏字符。

【问题讨论】:

  • 什么是“ANSI 编码”文件?
  • 我手头没有 Windows 安装(而且问题几乎可以肯定源于 Windows 奇怪的文件处理),但您应该使用模式 'w', encoding='utf8' 打开文件并写入 unicode对象(decode 的结果)'wb'(无编码)模式打开文件并写入str 对象(encode 的结果)。
  • 首先,token 真的是在ISO-8859-1 中编码的 str 吗?
  • @sarnold 检查this.
  • @PetrViktorin 似乎是这样。原始代码没有这一行,它在下一行以UnicodeDecodeError 崩溃。加入这条线后,它就不再抱怨了。

标签: python windows unicode utf-8 python-2.6


【解决方案1】:

当您使用codec.open('w', encoding='utf8') 打开文件时,将字节数组(str 对象)写入文件是没有意义的。相反,编写unicode 对象,如下所示:

corpusFile = codecs.open(filename, mode = 'w', encoding = 'utf-8')
# ...
tagged_token = '\xdcml\xe4ut'
tagged_token = tagged_token.decode('ISO-8859-1')
corpusFile.write(tagged_token)
corpusFile.write(u'\n')

这将写入与平台相关的行尾字符。

或者,打开一个二进制文件并写入已编码字符串的字节数组:

corpusFile = open(filename, mode = 'wb')
# ...
tagged_token = '\xdcml\xe4ut'
tagged_token = tagged_token.decode('ISO-8859-1')
corpusFile.write(tagged_token.encode('utf-8'))
corpusFile.write('\n')

这将编写独立于平台的 EOL。如果您想要platform-dependent EOL,请打印os.sep 而不是'\n'

注意encoding naming in Notepad++ is misleading:ANSI as UTF-8就是你想要的。

【讨论】:

  • 不走运 :( 请在问题中查看我对您的评论的回答。
  • @Metalcoder 更新了答案,并解释了为什么此代码有效;)如果您确定结果是 not UTF-8(如果 Notepad++ 将其命名为 ANSI as UTF-8 ,它 UTF-8),您可以在此答案中发布由两个替代executable 程序之一编写的文件的十六进制转储吗?
  • 不需要十六进制转储; Python:print repr(open("thefile", "rb").read(200)) 应该可以解决问题。
  • @Metalcoder John 列出了创建我提到的 hexdump 的方法(请参阅此答案下的第二条评论)。
  • @Metalcoder 您可能想阅读Unicode and character encodings。如果你执行了print repr(open(filename, "rb").read(200)),那么当你在这个答案中使用第一个和第二个程序时会输出什么?
【解决方案2】:

尝试使用 UTF-8 签名(又名 BOM)编写文件:

def storeTaggedCorpus(corpus, filename):
    corpusFile = codecs.open(filename, mode = 'w', encoding = 'utf-8-sig')
    for token in corpus:
        tagged_token = '/'.join(str for str in token)
        # print(type(tagged_token)); break
        # tagged_token = tagged_token.decode('cp1252')
        corpusFile.write(tagged_token)
        corpusFile.write(u"\n")
    corpusFile.close()

请注意,这只有在 tagged_token 是 unicode 字符串时才能正常工作。要检查这一点,请取消注释上述代码中的第一条注释 - 它应该打印 <type 'unicode'>

如果tagged_token 不是 unicode 字符串,那么您需要先使用第二个注释行对其进行解码。 (注意:我假设是“cp1252”编码,但如果您确定它是“iso-8859-1”,那么您当然需要更改它。)

【讨论】:

  • 哦,伙计,它会打印出 !!!我尝试在join 中切换到u'/',它抛出了一个UnicodeDecodeError。我没想到会这样,我要进行一些测试。
  • @Metalcoder。切换到u'/' 将不起作用,因为字符串的其余部分将无法正确解码。为此,请删除 print 语句并取消注释第二条注释,如上所示。
【解决方案3】:

如果您从文件中看到“损坏”的字符,您需要确保用于查看文件的任何内容都理解该文件是 UTF-8 编码的。

这段代码创建的文件:

import codecs
for enc in "utf-8 utf-8-sig".split():
    with codecs.open(enc + ".txt", mode = 'w', encoding = enc) as corpusFile:
        tagged_token = '\xdcml\xe4ut'
        tagged_token = tagged_token.decode('cp1252') # not 'ISO-8859-1'
        corpusFile.write(tagged_token) # write unicode objects
        corpusFile.write(u'\n')

因此被识别:

Notepad++(版本 5.7 (UNICODE)):不带 BOM 的 UTF-8、UTF-8
Firefox (7.0.1):西方 (ISO-8859-1)、Unicode (UTF-8)
记事本(Windows 7):UTF-8、UTF-8

将 BOM 放入 UTF-8 文件中,虽然在 Unix 系统上已弃用,但在 Windows 上,其他软件能够将您的文件识别为 UTF-8 编码的文件的机会要大得多。

【讨论】:

  • 我在发布此问题之前尝试发送 BOM,但遇到了同样的问题。但是BOM应该是找出文件编码的问题。我相信在其中存储一些东西时它不会产生任何影响。我错了吗?
猜你喜欢
  • 1970-01-01
  • 2015-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-28
  • 1970-01-01
  • 2021-04-05
  • 1970-01-01
相关资源
最近更新 更多