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


我在这里不明白什么?显然,我缺少一些至关重要的魔术(或良好的意识)。为了真正进行转换,一种类型的文本文件是什么?

我真正无法理解的是,如果您实际上无法获取Python,那么UTF-8表示法的意义是什么?识别它,当它来自外部时。也许我应该只将JSON转储字符串,然后使用它,因为它具有可表示性!更重要的是,当来自文件时,Python是否会识别并解码该Unicode对象的ASCII表示形式?如果是这样,我该怎么办?

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


#1 楼

用符号

u'Capit\xe1n\n'


“ \ xe1”仅表示一个字节。 “ \ x”告诉您“ e1”为十六进制。
当您将

Capit\xc3\xa1n


写入文件时,其中就有“ \ xc3”。这些是4个字节,在您的代码中,您全部读取了它们。显示它们时可以看到以下内容:

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


您可以看到反斜杠被反斜杠转义了。因此,您的字符串中有四个字节:“ \”,“ x”,“ c”和“ 3”。

编辑:

其他人在他们的答案中指出您只需要在编辑器中输入字符,然后您的编辑器就应该处理到UTF-8的转换并保存。

如果您实际上有这种格式的字符串,则可以使用string_escape编解码器进行解码将其转换为普通字符串:

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


结果是一个以UTF-8编码的字符串,其中重音字符由\xc3\xa1中写入的两个字节表示原始字符串。如果要使用unicode字符串,则必须使用UTF-8重新解码。

进行编辑:文件中没有UTF-8。要实际查看它的外观,请执行以下操作:

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


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

评论


那么,如果python可以使用utf-8编码格式读取文件,那又有什么意义呢?换句话说,python是否会在\ xc3中读取1个字节的ascii表示形式?

– Gregg Lind
09年1月29日在16:51

您的“那么,重点是什么...”问题的答案是“ Mu”。 (因为Python可以读取以UTF-8编码的文件)。对于第二个问题:\ xc3不是ASCII集的一部分。也许您的意思是“ 8位编码”。您对Unicode和编码感到困惑;没关系,很多。

–tzot
09年1月30日在12:16

尝试将其作为入门读物:joelonsoftware.com/articles/Unicode.html

–tzot
09年1月30日在12:16

注意:u'\ xe1'是一个Unicode代码点U + 00e1,可以根据字符编码使用1个或多个字节来表示(在utf-8中为2个字节)。 b'\ xe1'是一个字节(数字225),它可以表示的字母取决于用于解码的字符编码,例如,在cp1251中为б(U + 0431),在cp866中为с(U + 0441)等

– jfs
13年6月15日在6:31

令人惊讶的是,有这么多英国编码员说“只使用ascii”,却没有意识到英镑符号不是它。大多数人不知道ascii!=本地代码页(即latin1)。

–丹尼·史泰普(Danny Staple)
2013年9月5日在12:58

#2 楼

我发现打开文件时更容易指定编码,而不是搞乱编码和解码方法。 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.open函数是内置的open功能。内置的open函数仅在Python 3中支持编码参数,而在Python 2中不支持。

编辑:以前,此答案推荐使用编解码器模块。当混合使用read()readline()时,编解码器模块可能会导致问题,因此现在建议使用io模块。

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

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

评论


也可以完美地写文件,而不是用open(file,'w')做codecs.open(file,'w','utf-8')解决

–马特·康诺利
2011年3月4日在2:12



这是我一直在寻找的答案:)

–贾斯汀
2012年6月27日22:54

codecs.open(...)方法是否也完全符合open(...):样式,其中with会在所有操作完成后关闭文件?无论如何,它似乎仍然有效。

–try-catch-finally
13年4月4日在18:09

@ try-catch-finally是的。我一直在使用codecs.open(...)作为f:。

– Tim Swast
13年7月8日在14:27

我希望我可以投票一百次。在因大量混合数据而导致的编码问题上苦苦挣扎了几天,并且对编码感到困惑之后,这个答案就像沙漠中的水一样。希望我早点看到。

–迈克·吉拉德(Mike Girard)
13年7月21日在18:24

#3 楼

现在,您需要在Python3中使用open(Filename, 'r', encoding='utf-8')

[编辑于2016-02-10以获得所需的澄清]

Python3在其open函数中添加了编码参数。从此处收集了有关打开功能的以下信息: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支持的任何文本编码。
有关支持的编码列表,请参见codecs模块。


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

评论


您能否详细说明您的答案,并提供有关您提供的解决方案的更多说明?

–阿里巴松
16年2月10日在16:26

看起来这可以在Python 2中使用编解码器模块-codecs.open('somefile',encoding ='utf-8')stackoverflow.com/a/147756/149428

–泰勒·埃德米斯顿(Taylor Edmiston)
16年8月14日在1:43



#4 楼

因此,我找到了所需解决方案,它是:

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


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

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

评论


良好的响应,我已经测试了两个解决方案(codecs.open(file,“ r”,“ utf-8”)和简单的open(file,“ r”)。read()。decode(“ utf-8”)两者都运作良好。

–鹰
13年6月13日在9:03

我收到一个“ TypeError:预期的str,字节或os.PathLike对象,而不是_io.TextIOWrapper”任何想法,为什么?

– JinSnow
17年1月7日在21:37



我认为,考虑到支持投票的数量,最好接受第二个答案:)

–杰科特
17年4月4日在12:00

#5 楼

实际上,这对于我在Python 3.2中读取以UTF-8编码的文件很有效:

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


#6 楼

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


#7 楼

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


评论


+1 io比编解码器好得多。

–personal_cloud
17-09-27在20:58



是的,使用io更好;但是我用io.open('data.txt','w','utf-8')作为文件写了这样的with语句:并得到一个错误:TypeError:需要一个整数。在我将io.open('data.txt','w',encoding ='utf-8')更改为文件后:它起作用了。

–胡文van
18年1月2日,下午5:33



#8 楼

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

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


对于python支持的http服务器很有用。

#9 楼

您已经迷惑了编码的一般问题:我怎么知道文件是哪种编码?

答案:除非文件格式为此提供,否则您将无法识别。例如,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'


格雷格·林德(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”使用四个字符对最多0xFFFF(65535)的Unicode进行编码。

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

使用decode('string-escape')的解决方案确实有效,但是您必须知道使用了多少内存:三个乘以codecs.open()的使用量。

请记住,文件只是一个8位字节序列。位和字节都没有意义。是您说“ 65代表'A'”。由于\xc3\xa1应该变成“à”,但是计算机无法识别,因此必须通过指定在写入文件时使用的编码来告诉它。

评论


我认为这里缺少一些片段:文件f2包含:十六进制:0000000:4361 7069 745c 7863 335c 7861 316e 0a Capit \ xc3 \ xa1n。例如,codecs.open('f2','rb','utf-8')会在一个单独的字符中读取所有字符(预期)是否有任何方法可以用ascii写入文件?

– Gregg Lind
09年1月29日在17:21

#10 楼

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

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

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

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


,然后将此字符串粘贴到编辑器中,并确保使用Latin-1将其存储。在剪贴板不乱码的假设下,往返应该起作用。

#11 楼

\ x ..序列特定于Python。这不是通用的字节转义序列。

如何实际输入UTF-8编码的非ASCII取决于您的操作系统和/或编辑器。这是您在Windows中的操作方法。要使OS X输入带有重音符号的a,您可以先按Option + E,再按A,几乎OS X中的所有文本编辑器都支持UTF-8。

#12 楼

您还可以使用open()函数将其替换就位,从而改进原始partial函数以使用Unicode文件。该解决方案的优点在于您无需更改任何旧代码。它是透明的。

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


#13 楼

我试图使用Python 2.7.9解析iCal:


来自icalendar import Calendar


,但是我得到了:

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


(现在它可以打印图像了。)

#14 楼

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

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


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

至少对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 /(最后看)。