用Python写入UTF-8文件


204

我真的很困惑codecs.open function。当我做:

file = codecs.open("temp", "w", "utf-8")
file.write(codecs.BOM_UTF8)
file.close()

它给我错误

UnicodeDecodeError:'ascii'编解码器无法解码位置0的字节0xef:序数不在范围内(128)

如果我做:

file = open("temp", "w")
file.write(codecs.BOM_UTF8)
file.close()

它工作正常。

问题是为什么第一种方法会失败?以及如何插入宝?

如果第二种方法是正确的方法,那么使用的重点是codecs.open(filename, "w", "utf-8")什么?


54
不要在UTF-8中使用BOM。 请。
tchrist 2012年

7
@tchrist呵呵?为什么不?
Salman von Abbas

8
@SalmanPK BOM在UTF-8中不是必需的,只会增加复杂性(例如,您不能仅将BOM的文件和结果与有效文本连接在一起)。请参阅此问答;不要错过Q下的重大评论
Alois Mahdal

Answers:


271

我相信问题在于这codecs.BOM_UTF8是字节字符串,而不是Unicode字符串。我怀疑文件处理程序试图基于“我是将Unicode编写为UTF-8编码的文本,但是您给了我一个字节字符串!”来猜测您的意思是什么!

尝试直接为字节顺序标记(即Unicode U + FEFF)编写Unicode字符串,以便文件将其编码为UTF-8:

import codecs

file = codecs.open("lol", "w", "utf-8")
file.write(u'\ufeff')
file.close()

(这似乎给出了正确的答案-字节为EF BB BF的文件。)

编辑:S. Lott 建议使用“ utf-8-sig”作为编码要比自己直接编写BOM更好,但是在此我将保留此答案,因为它可以解释以前的问题。


警告:打开与打开不一样。如果您执行“从编解码器导入打开”,则不会与您只键入“打开”相同。
阿帕奇(

2
您也可以改用codecs.open('test.txt','w','utf-8-sig')
Beta于

1
我收到“ TypeError:必须为整数(类型为str)”。我不明白我们在这里做什么。有人可以帮忙吗?我需要在文本文件中附加一个字符串(段落)。写入之前是否需要先将其转换为整数?
Mugen '18

@Mugen:据我所知,我编写的确切代码可以正常工作。我建议您提出一个新问题,以确切显示您拥有的代码以及错误发生的位置。
乔恩·斯基特

@Mugen您需要致电codecs.open而不仅仅是open
Northben

179

阅读以下内容:http : //docs.python.org/library/codecs.html#module-encodings.utf_8_sig

做这个

with codecs.open("test_output", "w", "utf-8-sig") as temp:
    temp.write("hi mom\n")
    temp.write(u"This has ♭")

生成的文件为带有预期BOM的UTF-8。


2
谢谢。那行得通(Windows 7 x64,Python 2.7.5 x64)。当您以模式“ a”(附加)打开文件时,此解决方案效果很好。
Mohamad Fakih

这对我不起作用,Windows上的Python 3。我不得不使用open(file_name,'wb')作为bomfile来执行此操作:bomfile.write(codecs.BOM_UTF8),然后重新打开该文件以进行追加。
达斯汀·安德鲁斯

也许加上temp.close()
user2905353

2
@ user2905353:不是必需的;这是由的上下文管理处理的open
马修堡

11

@ S-Lott提供了正确的过程,但是在解释Unicode问题时,Python解释器可以提供更多的见解。

乔恩·斯凯特(Jon Skeet)对codecs模块是正确的(非常规)-它包含字节字符串:

>>> import codecs
>>> codecs.BOM
'\xff\xfe'
>>> codecs.BOM_UTF8
'\xef\xbb\xbf'
>>> 

挑选另一个尼特,它BOM具有标准的Unicode名称,可以输入以下形式:

>>> bom= u"\N{ZERO WIDTH NO-BREAK SPACE}"
>>> bom
u'\ufeff'

也可以通过unicodedata以下方式访问:

>>> import unicodedata
>>> unicodedata.lookup('ZERO WIDTH NO-BREAK SPACE')
u'\ufeff'
>>> 

8

我使用file * nix命令将未知字符集文件转换为utf-8文件

# -*- encoding: utf-8 -*-

# converting a unknown formatting file in utf-8

import codecs
import commands

file_location = "jumper.sub"
file_encoding = commands.getoutput('file -b --mime-encoding %s' % file_location)

file_stream = codecs.open(file_location, 'r', file_encoding)
file_output = codecs.open(file_location+"b", 'w', 'utf-8')

for l in file_stream:
    file_output.write(l)

file_stream.close()
file_output.close()

1
使用# coding: utf8而不是 # -*- coding: utf-8 -*-更容易记住。
show0k '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.