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’”、“‘把\ xc3 \ xa1n”)

print ss, ss8
print >> open('f1','w'), ss8


>>> file('f1').read()
'Capit\xc3\xa1n\n'

所以我在我最喜欢的编辑器f2文件中输入Capit\xc3\xa1n

然后:

>>> 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'

我哪里不明白了?显然,我忽略了一些至关重要的魔力(或良好的感觉)。在文本文件中输入什么才能得到正确的转换?

我在这里真正搞不懂的是,当它来自外部时,如果你不能让Python识别它,那么UTF-8表示的意义是什么。也许我应该只是JSON转储字符串,并使用它,因为它有一个asciiable表示!更重要的是,当这个Unicode对象从文件中传入时,是否存在Python能够识别和解码的ASCII表示形式?如果有,我怎么得到它?

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

\ x . .sequence是Python特有的。它不是通用的字节转义序列。

如何实际输入utf -8编码的非ascii取决于您的操作系统和/或编辑器。下面是在Windows中如何做的。对于OS X来说,要输入带有尖锐重音的一个,只需按选项 + E,然后是一个,而且OS X中几乎所有的文本编辑器都支持UTF-8。

在表示法中u'Capit\xe1n\n'(应该只是3中的'Capit\xe1n\n'。x,而必须在3.0和3.1),\xe1只代表一个字符。\x是一个转义序列,表明e1是十六进制。

在文本编辑器中将Capit\xc3\xa1n写入文件中意味着它实际上包含了\xc3\xa1。这是8个字节,代码会全部读取。我们可以通过显示结果来看到这一点:

# Python 3.x - reading the file as bytes rather than text,
# to ensure we see the raw data
>>> open('f2', 'rb').read()
b'Capit\\xc3\\xa1n\n'


# Python 2.x
>>> open('f2').read()
'Capit\\xc3\\xa1n\n'

相反,只需在编辑器中输入像á这样的字符,然后编辑器应该处理转换为UTF-8并保存它。

在2。x,实际上包含这些反斜杠转义序列的字符串可以使用string_escape编解码器解码:

# Python 2.x
>>> print 'Capit\\xc3\\xa1n\n'.decode('string_escape')
Capitán

结果是一个用UTF-8编码的str,其中重音字符由在原始字符串中写入\\xc3\\xa1的两个字节表示。要获得unicode结果,请再次使用UTF-8解码。

在3。x, string_escape编解码器被替换为unicode_escape,并且严格强制我们只能从str encodebytes,从bytes decodestrunicode_escape需要以bytes开始以便处理转义序列(反过来,它unicode_escape2它们);然后它将结果unicode_escape0和unicode_escape1视为unicode_escape3转义,而不是unicode_escape4转义。因此,我们必须做更多的工作:

# Python 3.x
>>> 'Capit\\xc3\\xa1n\n'.encode('ascii').decode('unicode_escape').encode('latin-1').decode('utf-8')
'Capitán\n'

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

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

在终端和应用程序之间粘贴文本是困难的,因为您不知道哪个程序将使用哪种编码来解释您的文本。你可以试试下面的方法:

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

然后将这个字符串粘贴到编辑器中,并确保它使用Latin-1存储它。假设剪贴板不会使字符串乱码,那么往返应该可以工作。

您曾经遇到过编码的一般问题:如何判断文件采用的是哪种编码?

答:你不能除非文件格式提供了这一点。例如,XML的开头是:

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

此标头经过精心选择,以便无论编码如何都可以读取。在你的例子中,没有这样的提示,因此你的编辑器和Python都不知道发生了什么。因此,您必须使用codecs模块并使用codecs.open(path,mode,encoding),它提供了Python中缺少的位。

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

UTF-8的重点是能够将21位字符(Unicode)编码为8位数据流(因为这是世界上所有计算机都能处理的唯一事情)。但是由于大多数操作系统早于Unicode时代,它们没有合适的工具将编码信息附加到硬盘上的文件。

下一个问题是Python中的表示。这在heikogerlach的评论中得到了完美的解释。您必须理解控制台只能显示ASCII。为了显示Unicode或任何东西>= charcode 128,它必须使用一些转义的方法。在编辑器中,您不能输入转义的显示字符串,而是输入字符串的含义(在这种情况下,您必须输入变音符并保存文件)。

也就是说,你可以使用Python函数eval()将转义字符串转换为字符串:

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

如您所见,字符串“\xc3”已被转换为单个字符。这现在是一个8位字符串,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”使用四个字符将Unicode编码到0xFFFF(65535)。

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

使用decode('string-escape')的解决方案确实有效,但你必须意识到你使用了多少内存:使用codecs.open()的三倍。

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

所以,我找到了我想要的解决方案,那就是:

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

这里有一些不寻常的编解码器很有用。这种特殊的读取允许从Python中获取UTF-8表示,将它们复制到ASCII文件中,并将它们读入Unicode。在“字符串转义”解码下,斜杠不会加倍。

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

与其混淆.encode.decode,不如在打开文件时指定编码。在Python 2.6中添加的io模块提供了一个io.open函数,它允许指定文件的encoding

假设文件用UTF-8编码,我们可以使用:

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

然后f.read返回一个解码后的Unicode对象:

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

在3。在2.x中,io.open函数是内置open函数的别名,该函数支持encoding参数(在2.x中不支持)。

我们也可以使用__ABC0来自codecs标准库模块:

>>> import codecs
>>> f = codecs.open("test", "r", "utf-8")
>>> f.read()
u'Capit\xe1l\n\n'

但是请注意,这个会导致问题,当混合__ABC0和readline().;

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

这适用于在Python 3.2中读取UTF-8编码的文件:

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

为了读入Unicode字符串,然后发送到HTML,我这样做:

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

适用于python支持的http服务器。

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

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

Python3将编码形参添加到其open函数中。关于open函数的以下信息从这里收集

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

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

因此,通过将encoding='utf-8'作为参数添加到open函数中,文件的读写都以utf8完成(这也是Python中所有操作的默认编码)。

我试图使用Python 2.7.9解析iCal:

导入日历

但我得到:

 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"))

(现在它可以打印liké á böss。)

你也可以改进原来的open()函数,通过使用partial函数替换它来处理Unicode文件。这个解决方案的美妙之处在于您不需要更改任何旧代码。它是透明的。

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

除了codecs.open()之外,io.open()可以在这两个函数中使用。X和3。X来读写文本文件。例子:

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

我找到了最简单的方法,将整个脚本的默认编码改为'UTF-8':

import sys
reload(sys)
sys.setdefaultencoding('utf8')

任何openprint或其他语句将只使用utf8

至少适用于Python 2.7.9

Thx转到https://markhneedham.com/blog/2015/05/21/python-unicodeencodeerror-ascii-codec-cant-encode-character-uxfc-in-position-11-ordinal-not-in-range128/(看结尾)。