Python,将输出编码为UTF-8

5
我有一个定义,用于构建由UTF-8编码字符组成的字符串。 输出文件使用'w+', "utf-8"参数打开。
然而,当我尝试使用x.write(string)时,出现UnicodeEncodeError: 'ascii' codec can't encode character u'\ufeff' in position 1: ordinal not in range(128)错误。
我猜想这是因为通常情况下您会执行例如`print(u'something')`。但是我需要使用变量,并且u'_'中的引号会使其无效...
有什么建议吗?
编辑:实际代码在此处:
source = codecs.open("actionbreak/" + target + '.csv','r', "utf-8")
outTarget = codecs.open("actionbreak/" + newTarget, 'w+', "utf-8")
x = str(actionT(splitList[0], splitList[1]))
outTarget.write(x)

这个功能的主要作用是构建大量类似以下格式的字符串:

[日木曜 Deliverables]= CASE WHEN things = 11 THEN C ELSE 0 END


1
你尝试过使用 x.write(string.encode('utf-8')) 吗? - xgord
请展示您正在使用的打开文件的实际代码以及您从哪里获取了 u'\ufeff' - Wooble
谢谢,我添加了更多细节。 - Razzle Dazzle
错误是发生在write行还是str行上? - Mark Ransom
3个回答

5
在Python 2.x中有两种字符串类型:字节字符串和Unicode字符串。第一种包含字节,而第二种包含Unicode代码点。很容易确定字符串的类型-带有u前缀的是Unicode字符串:
# byte string
>>> 'abc'
'abc'

# unicode string:
>>> u'abc абв'
u'abc \u0430\u0431\u0432'

'

"abc"字符相同,因为它们都在ASCII范围内。\u0430是一个Unicode码点,它超出了ASCII范围。“码点”是Python内部对Unicode码点的表示,不能直接保存到文件中。需要首先对其进行编码以生成字节串。以下是编码后的Unicode字符串(一旦被编码,它就变成了一个字节串):

'
>>> s = u'abc абв'
>>> s.encode('utf8')
'abc \xd0\xb0\xd0\xb1\xd0\xb2'

现在可以将这个编码字符串写入文件:

>>> s = u'abc абв'
>>> with open('text.txt', 'w+') as f:
...     f.write(s.encode('utf8'))

现在,重要的是记住我们写入文件时使用了什么编码。因为为了能够读取数据,我们需要对内容进行解码。以下是未经解码的数据示例:

>>> with open('text.txt', 'r') as f:
...     content = f.read()
>>> content
'abc \xd0\xb0\xd0\xb1\xd0\xb2'

你看,我们有编码字节,与 s.encode('utf8') 中完全相同。要解码它,需要提供编码名称:

>>> content.decode('utf8')
u'abc \u0430\u0431\u0432'

解码后,我们得到了具有Unicode代码点的Unicode字符串。
>>> print content.decode('utf8')
abc абв

他正在使用 codecs.open(),不需要显式编码/解码。 - JAB
虽然这并没有解决我的问题,但我很感激这个教训。这是非常有趣的信息,我以前不知道。 - Razzle Dazzle

5

您是否在使用codecs.open()?Python 2.7内置的open()不支持特定编码,这意味着您必须手动对非ASCII字符串进行编码(正如其他人所指出的那样),但是codecs.open()支持该功能,可能比手动编码所有字符串更容易。


由于您实际上正在使用codecs.open(),根据您添加的代码并经过一些自己查找的工作,我建议尝试使用编码"utf-8-sig"打开输入和/或输出文件,这将自动处理UTF-8的BOM(请参见http://docs.python.org/2/library/codecs.html#encodings-and-unicode,在该部分的底部)。我认为这只对输入文件有影响,但如果没有这些组合(utf-8-sig/utf-8,utf-8/utf-8-sig,utf-8-sig/utf-8-sig)有效,则我认为最可能的情况是您的输入文件以不同的Unicode格式编码,并带有BOM,因为Python默认的UTF-8编解码器将BOM解释为常规字符,因此输入不会有问题,但输出可能会有问题。
我刚刚注意到一个问题,当你使用codecs.open()时,它期望的是一个Unicode字符串而不是编码后的字符串;尝试使用x = unicode(actionT(splitList[0], splitList[1]))
你也可能会遇到错误,当尝试解码Unicode字符串时(参见http://wiki.python.org/moin/UnicodeEncodeError),但我认为这只有在actionT()或者列表分割过程中对Unicode字符串进行了某些操作导致其被视为非Unicode字符串时才会发生。

是的,我正在使用codecs.open()。即使如此,似乎我仍然遇到了这个错误。 - Razzle Dazzle
@RazzleDazzle 这是一个BOM问题。我对我的答案进行了一些补充,请尝试并查看哪些有效(如果有的话)。 - JAB
这似乎是在运行时导致字符串出错的原因: [テスト]= CASE WHEN T THEN c ELSE 0 END看起来日语字符引起了编码错误。输入文件的编码由我控制。我一直在使用UTF-8编码的.csv作为输入,以保留日语字符。 - Razzle Dazzle
将编码为UNICODE字符串可以成功处理所有字符串进入输出文件,但似乎不能在此过程中保留UTF-8特定字符。 我之前发布的日语字符串现在变成了: [テスト]= CASE WHEN T 话虽如此,你确实让我朝着正确的方向前进。非常感谢! - Razzle Dazzle
好的。你做得很好。谢谢! - Razzle Dazzle
显示剩余3条评论

1
xgord是正确的,但为了进一步说明,值得注意的是什么是\ufeff。它被称为BOM或字节顺序标记,基本上它是Unicode早期时代的回调,当时人们无法决定他们想要的Unicode走哪条路。现在,所有的Unicode文档都以\ufeff或\uffef开头,这取决于他们决定如何排列字节。
如果你在第一个位置遇到这些字符的错误,你可以确定问题是你没有尝试将其解码为utf-8,并且文件可能仍然正常。

您稍有不准,BOM将始终为\ufeff。 BOM的实际编码可能会有所不同,但代码点始终为U+FEFF。 如果您将其读取为\uffef,则您的字节序已经翻转了。 - JAB
如果是 b'\xef\xbb\xbf',那么编码当然是UTF-8。 - JAB

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