Unicode(UTF-8)读取和写入Python文件

理解阅读和写文本到一个文件(Python 2.4),我有一些脑力衰竭。

# The string, which has an a-acute in it. ss = u'Capit\xe1n' ss8 = ss.encode('utf8') repr(ss), repr(ss8) 

(“u'Capit \ xe1n'”,“'Capit \ xc3 \ xa1n'”)

 print ss, ss8 print >> open('f1','w'), ss8 >>> file('f1').read() 'Capit\xc3\xa1n\n' 

所以我inputCapit\xc3\xa1n到我最喜欢的编辑器中,在文件f2中。

然后:

 >>> open('f1').read() 'Capit\xc3\xa1n\n' >>> open('f2').read() 'Capit\\xc3\\xa1n\n' >>> open('f1').read().decode('utf8') u'Capit\xe1n\n' >>> open('f2').read().decode('utf8') u'Capit\\xc3\\xa1n\n' 

我在这里不了解什么? 显然有一些重要的魔法(或者说是有意义的)我失踪了。 一个人input到文本文件中以获得正确的转换?

我真的没有在这里讨论什么,UTF-8的代表性是什么,如果你真的不能让Python认出它,当它来自外部。 也许我应该只是JSON转储string,并使用它,因为它有一个asciiable表示! 更重要的是,当从一个文件进入时,Python会识别并解码这个Unicode对象的ASCII表示吗? 如果是这样,我怎么得到它?

 >>> print simplejson.dumps(ss) '"Capit\u00e1n"' >>> print >> file('f3','w'), simplejson.dumps(ss) >>> simplejson.load(open('f3')) u'Capit\xe1n' 

在记号中

 u'Capit\xe1n\n' 

“\ xe1”只代表一个字节。 “\ x”告诉你“e1”是hex的。 当你写

 Capit\xc3\xa1n 

进入你的文件,你有“\ xc3”在里面。 这些是4个字节,在你的代码中,你可以全部阅读。 你可以看到这个当你显示他们:

 >>> open('f2').read() 'Capit\\xc3\\xa1n\n' 

您可以看到反斜杠被反斜杠转义。 所以你的string中有四个字节:“\”,“x”,“c”和“3”。

编辑:

正如其他人在他们的答案中指出,你应该只是在编辑器中input字符,然后你的编辑器应该处理转换为UTF-8并保存。

如果你实际上有这种格式的string,你可以使用string_escape编解码器将其解码为一个正常的string:

 In [15]: print 'Capit\\xc3\\xa1n\n'.decode('string_escape') Capitán 

结果是以UTF-8编码的string,其中重音字符由在原始string中写入\\xc3\\xa1的两个字节表示。 如果你想要一个Unicodestring,你必须用UTF-8再次解码。

编辑:你的文件中没有UTF-8。 要真正看到它是怎么样的:

 s = u'Capit\xe1n\n' sutf8 = s.encode('UTF-8') open('utf-8.out', 'w').write(sutf8) 

将文件utf-8.out的内容与您用编辑器保存的文件的内容进行比较。

而不是乱搞编码和解码方法,我发现打开文件时指定编码更容易。 io模块 (在Python 2.6中添加)提供了一个io.open函数,它有一个编码参数。

使用io模块的open方法。

 >>>import io >>>f = io.open("test", mode="r", encoding="utf-8") 

然后在调用f的read()函数之后,返回一个编码的Unicode对象。

 >>>f.read() u'Capit\xe1l\n\n' 

请注意,在Python 3中, io.read函数是内置read函数的别名。 内置的读取函数只支持Python 3中的编码参数,而不支持Python 2。

编辑:以前这个答案推荐编解码器模块。 混合read()readline() , 编解码器模块可能会导致问题 ,所以现在这个答案build议使用io模块。

使用编解码器模块中的打开方法。

 >>>import codecs >>>f = codecs.open("test", "r", "utf-8") 

然后在调用f的read()函数之后,返回一个编码的Unicode对象。

 >>>f.read() u'Capit\xe1l\n\n' 

如果你知道一个文件的编码,使用编解码器软件包将不那么容易混淆。

请参阅http://docs.python.org/library/codecs.html#codecs.open

所以,我find了我正在寻找的解决scheme,即:

 print open('f2').read().decode('string-escape').decode("utf-8") 

这里有一些非常有用的编解码器。 这个特定的读取允许从Python内部获取UTF-8表示,将它们复制到一个ASCII文件中,并将它们读入Unicode。 在“string转义”解码下,斜线不会翻倍。

这允许我想象的那种往返行程。

 # -*- 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() 

现在你只需要在Python3中open(Filename, 'r', encoding='utf-8')

[编辑于2016-02-10请求澄清]

Python3将编码参数添加到其打开的函数。 以下有关打开函数的信息从这里收集: https : //docs.python.org/3/library/functions.html#open

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

编码是用于解码或编码文件的编码的名称。 这只能用于文本模式。 默认的编码是依赖于平台的(无论locale.getpreferredencoding()返回),但是可以使用Python支持的任何文本编码 。 有关支持的编码列表,请参阅编解码器模块。

所以,通过在open函数中joinencoding='utf-8'作为参数,文件的读写操作都是以utf8的方式完成的(这也是Python所做的所有事情的默认编码)。

实际上,这对我来说是用Python 3.2读取一个UTF-8编码的文件:

 import codecs f = codecs.open('file_name.txt', 'r', 'UTF-8') for line in f: print(line) 

要读取一个Unicodestring,然后发送到HTML,我这样做:

 fileline.decode("utf-8").encode('ascii', 'xmlcharrefreplace') 

用于python驱动的http服务器。

那么,你最喜欢的文本编辑器没有意识到\xc3\xa1应该是字符文字,但是它将它们解释为文本。 这就是为什么你在最后一行得到双反斜杠 – 现在是你的文件中真正的反斜杠+ xc3等等。

如果你想用Python读写编码文件,最好使用编解码器模块。

在terminal和应用程序之间粘贴文本很困难,因为您不知道哪个程序将使用哪种编码来解释您的文本。 你可以试试以下内容:

 >>> s = file("f1").read() >>> print unicode(s, "Latin-1") Capitán 

然后将此string粘贴到您的编辑器中,并确保它使用Latin-1进行存储。 假设剪贴板没有乱码,往返应该是正常的。

你已经偶然发现了编码的一般问题:我怎么知道文件是在哪一种编码?

答: 除非文件格式规定了这个, 否则你不能。 XML,例如,开始于:

 <?xml encoding="utf-8"?> 

这个头文件是经过精心挑选的,所以无论编码如何都可以读取。 在你的情况下,没有这样的提示,因此你的编辑和Python都不知道发生了什么。 因此,您必须使用codecs模块并使用codecs.open(path,mode,encoding)来提供Python中缺less的位。

至于你的编辑器,你必须检查它是否提供了一些方法来设置文件的编码。

UTF-8的意义在于能够将21位字符(Unicode)编码为8位数据stream(因为这是世界上所有电脑都能处理的唯一的东西)。 但是由于大多数操作系统早于Unicode时代,他们没有合适的工具将编码信息附加到硬盘上的文件中。

下一个问题是在Python中的表示。 这在heikogerlach的评论中得到了完美的解释。 您必须了解您的控制台只能显示ASCII。 为了显示Unicode或者其他任何东西> = charcode 128,它必须使用一些转义的手段。 在你的编辑器中,你不能input转义的显示string,而是string的意思(在这种情况下,你必须input变音符号并保存文件)。

也就是说,您可以使用Python函数eval()将转义string转换为string:

 >>> x = eval("'Capit\\xc3\\xa1n\\n'") >>> x 'Capit\xc3\xa1n\n' >>> x[5] '\xc3' >>> len(x[5]) 1 

正如你所看到的,string“\ xc3”已经变成了一个字符。 这现在是一个8位string,UTF-8编码。 要获得Unicode:

 >>> x.decode('utf-8') u'Capit\xe1n\n' 

格雷格·林德问:我认为这里有一些遗漏:文件f2包含:hex:

 0000000: 4361 7069 745c 7863 335c 7861 316e Capit\xc3\xa1n 

codecs.open('f2','rb', 'utf-8') ,例如,读取它们全部在一个单独的字符(预期)是否有任何方法来写入一个ASCII文件将工作?

答:这取决于你的意思。 ASCII不能代表> 127的字符。所以你需要一些方法来说“接下来的几个字符意味着什么特殊的东西”,这就是序列“\ x”所做的。 它说:接下来的两个字符是单个字符的代码。 “\ u”使用四个字符来编码最高达0xFFFF(65535)的Unicode。

所以你不能直接写Unicode到ASCII(因为ASCII不包含相同的字符)。 你可以把它写成string转义(如在f2中)。 在这种情况下,文件可以表示为ASCII。 或者你可以把它写成UTF-8,在这种情况下,你需要一个8位的安全stream。

使用decode('string-escape')解决scheme确实可行,但是您必须知道您使用了多less内存:使用codecs.open()

记住一个文件只是一个8位的字节序列。 位和字节都没有意义。 你是谁说“65意味着'A'”。 由于\xc3\xa1应该变成“à”,但计算机没有办法知道,所以必须通过指定写入文件时使用的编码来告诉它。

\ x …序列是Python专用的东西。 这不是一个通用的字节转义序列。

如何以UTF-8编码的非ASCII码实际input取决于您的操作系统和/或您的编辑器。 这是你如何在Windows中做到这一点 。 对于OS Xinput一个尖锐的重音,你可以select + E ,然后A ,OS X中几乎所有的文本编辑器都支持UTF-8。

除了codecs.open() ,可以使用io.open()来使用Python2或Python3读取/写入unicode文件

 import io text = u'á' encoding = 'utf8' with io.open('data.txt', 'w', encoding=encoding, newline='\n') as fout: fout.write(text) with io.open('data.txt', 'r', encoding=encoding, newline='\n') as fin: text2 = fin.read() assert text == text2 

你也可以改进原来的open()函数来处理Unicode文件,通过使用partial函数replace它。 这个解决scheme的优点是你不需要改变任何旧的代码。 这是透明的。

 import codecs import functools open = functools.partial(codecs.open, encoding='utf-8') 

我试图用Python 2.7.9parsingiCal :

从icalendar导入日历

但是我得到:

  Traceback (most recent call last): File "ical.py", line 92, in parse print "{}".format(e[attr]) UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 7: ordinal not in range(128) 

它只是固定的:

 print "{}".format(e[attr].encode("utf-8")) 

(现在可以打印比伯了。)