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'
如果你知道一个文件的编码,使用编解码器软件包将不那么容易混淆。
所以,我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"))
(现在可以打印比伯了。)