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

Unicode (UTF-8) reading and writing to files in Python

提问人:Gregg Lind 提问时间:1/29/2009 最后编辑:Peter MortensenGregg Lind 更新时间:9/4/2022 访问量:1100153

问:

我在理解读取和写入文件文本时遇到了一些大脑故障 (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'

因此,我在文件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'

我在这里有什么不明白的?显然,我缺少一些重要的魔力(或良好的感觉)。在文本文件中输入什么才能获得正确的转换?

我在这里真正没有弄清楚的是,UTF-8 表示的意义是什么,如果你不能真正让 Python 识别它,当它来自外部时。也许我应该将字符串 JSON 转储,并改用它,因为它具有可识别的表示形式!更重要的是,当从文件传入时,是否有 Python 可以识别和解码的 Unicode 对象的 ASCII 表示形式?如果是这样,我该如何获得?

>>> print simplejson.dumps(ss)
'"Capit\u00e1n"'
>>> print >> file('f3','w'), simplejson.dumps(ss)
>>> simplejson.load(open('f3'))
u'Capit\xe1n'
python unicode utf-8 io

评论

0赞 Karl Knechtel 8/30/2023
要理解的重要一点是,这是一个正确的结果,并且该字符串已经包含您正在寻找的特殊字符。它仅用转义序列表示。这里的基本问题实际上与如何读取或写入文件以及指定编码无关,因为代码已经显示了如何正确地执行此操作。u'Capit\xe1n\n'

答:

4赞 ʞɔıu 1/29/2009 #1

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

您实际输入 UTF-8 编码的非 ASCII 的方式取决于您的操作系统和/或编辑器。以下是在 Windows 中执行此操作的方法。要使 OS X 输入带有尖锐口音的 a,您只需按 + ,然后按 ,OS X 中几乎所有文本编辑器都支持 UTF-8。optionEA

124赞 unbeknown 1/29/2009 #2

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

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

# 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 编码的字符,其中重音字符由写入原始字符串的两个字节表示。要获得结果,请使用 UTF-8 再次解码。str\\xc3\\xa1unicode

在 3.x 中,编解码器被替换为 ,并且严格强制执行我们只能从 a 到 和 from to 。 需要以 a 开头才能处理转义序列(反之,它会添加它们);然后它将处理生成的 and 作为字符转义而不是字节转义。因此,我们必须做更多的工作:string_escapeunicode_escapeencodestrbytesdecodebytesstrunicode_escapebytes\xc3\xa1

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

评论

0赞 Gregg Lind 1/30/2009
那么,如果 python 可以使用它读取文件,那么 utf-8 编码格式的意义何在?换句话说,python 将在 \xc3 中读取为 1 字节的任何 ascii 表示形式吗?
4赞 tzot 1/30/2009
你的答案是“那么,有什么意义......”问题是“Mu”。(因为 Python 可以读取以 UTF-8 编码的文件)。对于第二个问题:\xc3 不是 ASCII 集的一部分。也许您的意思是“8 位编码”。您对 Unicode 和编码感到困惑;没关系,很多都是。
9赞 tzot 1/30/2009
试着把它作为入门读物:joelonsoftware.com/articles/Unicode.html
0赞 jfs 6/15/2013
注意:是一个 Unicode 代码点 U+00e1,可以使用 1 个或多个字节表示,具体取决于字符编码(在 UTF-8 中为 2 个字节)。 是一个字节(一个数字 225),它可以表示什么字母(如果有的话)取决于用于解码它的字符编码,例如,它在 cp1251 中是 б (U+0431),在 cp866 中是 сU+0441 等。u'\xe1'b'\xe1'
13赞 Danny Staple 9/5/2013
令人惊讶的是,有多少英国程序员说“只需使用 ascii”,然后没有意识到 £ 符号不是它。大多数人不知道 ascii!=local code page (即 latin1)。
6赞 Torsten Marek 1/29/2009 #3

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

如果要在 Python 中读取和写入编码文件,最好使用 codecs 模块。

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

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

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

评论

0赞 Nathan B 3/19/2023
这是针对 python 2 的
6赞 Aaron Digulla 1/30/2009 #4

您偶然发现了编码的一般问题:如何判断文件的编码方式?

答:除非文件格式提供此功能,否则您不能这样做。例如,XML 的开头为:

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

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

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

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'

格雷格·林德(Gregg Lind)问道: 我认为这里缺少一些部分: 文件 f2 包含: 十六进制:

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

评论

0赞 Gregg Lind 1/30/2009
我认为这里缺少一些部分:文件 f2 包含:十六进制:0000000:4361 7069 745c 7863 335c 7861 316e 0a Capit\xc3\xa1n。codecs.open('f2','rb', 'utf-8') ,例如,将它们全部读取到一个单独的字符中(预期) 有没有办法以 ascii 格式写入文件?
18赞 Gregg Lind 1/30/2009 #5

因此,我找到了我正在寻找的解决方案,即:

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

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

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

875赞 Tim Swast 5/10/2009 #6

打开文件时,不要弄乱 和 ,而是指定编码。Python 2.6 中添加的 io 模块提供了一个函数,该函数允许指定文件的 ..encode.decodeio.openencoding

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

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

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

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

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

我们还可以使用 open from codecs 标准库模块

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

但请注意,在混合 read() 和 readline() 时,这可能会导致问题

评论

74赞 Matt Connolly 3/4/2011
也非常适合编写文件,而不是解决open(file,'w')codecs.open(file,'w','utf-8')
6赞 try-catch-finally 3/5/2013
该方法是否也完全符合样式,即在完成所有操作后关心关闭文件?无论如何,它似乎有效。codecs.open(...)with open(...):with
2赞 Tim Swast 7/8/2013
@try-catch-finally 是的。我一直在用。with codecs.open(...) as f:
7赞 Mike Girard 7/22/2013
我希望我能投一百票。在为大量混合数据引起的编码问题苦恼了几天之后,对编码进行了斗鸡眼的阅读,这个答案就像沙漠中的水。希望我早点看到它。
0赞 Pat Grady 2/8/2018
很棒的收获!我试图清理下游的代码;我直接找到了问题的根源io.open(filename,'r',encoding='utf-8') as file:
14赞 Ricardo 2/9/2012 #7
# -*- 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()
20赞 Sina 8/19/2014 #8

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

import codecs
f = codecs.open('file_name.txt', 'r', 'UTF-8')
for line in f:
    print(line)
6赞 praj 9/18/2014 #9

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

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

对 python 驱动的 http 服务器很有用。

83赞 Dakusan 2/11/2016 #10

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

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

Python3 在其 open 函数中添加了 encoding 参数。有关打开函数的以下信息从此处收集: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)

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

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

评论

0赞 abarisone 2/11/2016
您能否详细说明您的答案,并添加有关您提供的解决方案的更多描述?
3赞 Taylor D. Edmiston 8/14/2016
看起来这在 python 2 中使用编解码器模块可用 - stackoverflow.com/a/147756/149428codecs.open('somefile', encoding='utf-8')
1赞 Alexx Roche 5/10/2016 #11

我试图使用 Python 2.7.9 解析 iCal

from icalendar import 日历

但我得到了:

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

3赞 hipertracker 12/8/2016 #12

您还可以通过使用该函数就地替换原始函数来改进原始函数以处理 Unicode 文件。此解决方案的优点在于您无需更改任何旧代码。它是透明的。open()partial

import codecs
import functools
open = functools.partial(codecs.open, encoding='utf-8')
9赞 Ryan 6/21/2017 #13

除此之外,还可以在 2.x 和 3.x 中用于读取和写入文本文件。例:codecs.open()io.open()

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

评论

1赞 personal_cloud 9/28/2017
+1 IO 比编解码器好得多。
0赞 Evan Hu 1/2/2018
是的,使用 io 更好;但是我写了这样的 with 语句并得到了一个错误:.在我改成之后,它起作用了。with io.open('data.txt', 'w', 'utf-8') as file:TypeError: an integer is requiredwith io.open('data.txt', 'w', encoding='utf-8') as file:
-2赞 dr0i 12/17/2019 #14

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

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

any 或其他语句将只使用 .openprintutf8

至少适用于.Python 2.7.9

谢谢 https://markhneedham.com/blog/2015/05/21/python-unicodeencodeerror-ascii-codec-cant-encode-character-uxfc-in-position-11-ordinal-not-in-range128/(看最后)。