我知道类似的问题已被问过一百万次,但尽管阅读了其中许多问题但我找不到适合我情况的解决方案。
我有一个django应用程序,我在其中创建了一个管理脚本。这个脚本读取一些文本文件,然后将它们输出到终端(稍后会对内容做更多有用的东西,但我还在测试它),并且字符出现了像\xc3\xa5
这样的转义序列而不是预期的å
。由于该转义序列意味着Ã¥
,这是å
由于编码问题而常见的误解,我怀疑至少有两个地方出错了。但是,我无法弄清楚在哪里 - 我已经检查了所有可能的罪魁祸首:
echo $LANG
提供en_US.UTF-8
file *
在它们所在的目录中导致所有条目被列为“UTF-8 Unicode文本”,除了一个,它不包含任何非ASCII字符并列为“ASCII文本”。在该文件上运行iconv -f ascii -t utf8 thefile.txt > utf8.txt
会产生另一个带有ASCII文本编码的文件。# -*- encoding: utf-8 -*-
;前面唯一的一行是#!/usr/bin/python3
,但是我已经尝试过将Python更改为.../python
或者完全将其删除以将其留给Django,但没有结果。我真的想不出在其他任何地方寻找链中的非UTF-8链接。我可能在哪里错过了更改为UTF-8的设置?
为了完整性:我正在使用lines = file.readlines()
读取文件并使用标准print()
函数进行打印。两端都没有手动编码或解码。
回应评论中的静止:
print(sys.getdefaultencoding(), sys.stdout.encoding, f.encoding)
为所有文件提供('ascii', 'UTF-8', None)
。print(lines[0].strip())
工作正常,但print(lines[0].strip(), lines[1].strip())
没有。添加.decode('utf-8')
会产生一个元组,其中两个字符串都标有前置u
和\xe5
(å
的正确转义序列)而不是之前的奇数字符 - 但我可以弄清楚如何将它们打印为常规字符串,没有转义字符。我已经测试了对.decode('utf-8')
的另一个调用以及str()
的封装,但两个都失败,UnicodeEncodeError
抱怨\xe5
无法在ascii中编码。由于单个字符串正常工作,我不知道还有什么可以测试。SSCCE:
# -*- coding: utf-8 -*-
import os, sys
for root,dirs,files in os.walk('txt-songs'):
for filename in files:
with open(os.path.join(root,filename)) as f:
print(sys.getdefaultencoding(), sys.stdout.encoding, f.encoding)
lines = f.readlines()
print(lines[0].strip()) # works
print(lines[0].strip(), lines[1].strip()) # does not work
答案 0 :(得分:2)
这里的一个大问题是你正在混合使用Python 2和Python 3.特别是,你已经编写了Python 3代码,而你正试图在Python 2.7中运行它。但是在此过程中还存在一些其他问题。所以,让我试着解释一切出错的问题。
我开始编译SSCCE,并且很快发现只有在我尝试在元组中打印值时才出现问题。换句话说,
print(lines[0].strip())
工作正常,但print(lines[0].strip(), lines[1].strip())
没有。
这里的第一个问题是元组(或任何其他集合)的str
包含其元素的repr
,而不是str
。解决此问题的简单方法是不打印集合。在这种情况下,根本没有理由打印元组;你有一个唯一的原因是你已经建立了它用于打印。做这样的事情:
print '({}, {})'.format(lines[0].strip(), lines[1].strip())
如果您已在变量中拥有集合,并且想要打印出每个元素的str,则必须明确地执行此操作。您可以使用以下方法打印每个str的repr:
print tuple(map(str, my_tuple))
...或直接打印每个的str:
print '({})'.format(', '.join(map(str, my_tuple)))
请注意,我上面使用的是Python 2语法。那是因为如果你真的使用了Python 3,那么首先就没有元组,也就没有必要调用str
。
你有一个Unicode字符串。在Python 3中,unicode
和str
是相同的类型。但是在Python 2中,bytes
和str
属于同一类型,unicode
是不同的类型。因此,在2.x中,您还没有str
,这就是您需要致电str
的原因。
Python 2也是print(lines[0].strip(), lines[1].strip())
打印元组的原因。在Python 3中,这是对print
函数的调用,其中两个字符串作为参数,因此它将打印出由空格分隔的两个字符串。在Python 2中,它是一个带有一个参数的print
语句,它是一个元组。
如果要在2.x和3.x中编写相同的代码,则需要避免打印多个参数,或者使用six.print_
之类的包装,或者执行{ {1}},或者做一些丑陋的事情,例如添加额外的括号以确保你的元组在两个版本中都是元组。
所以,在3.x中,你有from __future__ import print_function
个对象,你只需将它们打印出来。在2.x中,您有str
个对象,并且您正在打印他们的unicode
。您可以更改它以打印出他们的repr
,或者首先避免打印元组......但这仍然无济于事。
为什么呢?好吧,在任一版本中打印任何内容,只需在其上调用str
,然后将其传递给str
。但在3.x中,sys.stdio.write
表示str
,unicode
表示sys.stdio
;在2.x中,TextIOWrapper
表示str
,bytes
是二进制sys.stdio
。
因此,最终发生的伪代码是:
file
而且,如你所见,那些会做不同的事情,因为:
sys.stdio.wrapped_binary_file.write(s.encode(sys.stdio.encoding, sys.stdio.errors)) sys.stdio.write(s.encode(sys.getdefaultencoding()))
收益print(sys.getdefaultencoding(), sys.stdout.encoding, f.encoding)
您可以使用('ascii', 'UTF-8', None)
或io.TextIOWrapper
然后使用codecs.StreamWriter
或print >>f, …
代替f.write(…)
来模拟Python 3,或者您可以显式编码所有print
个对象都是这样的:
unicode
但实际上,处理所有这些问题的最佳方法是在Python 3解释器而不是Python 2解释器中运行现有的Python 3代码。
如果您想要或需要使用Python 2.7,那很好,但您必须编写Python 2代码。如果你想编写Python 3代码,那很好,但你必须运行Python 3.3。如果你真的想要编写在两者中都能正常运行的代码,你可以 ,但这是额外的工作,并且需要更多的知识。
有关详细信息,请参阅What's New In Python 3.0(“打印是一个函数”和“文本与数据而不是Unicode与8位”部分),尽管这是从解释3的角度编写的。 .x到2.x用户,这是你需要的后退。 Unicode HOWTO的3.x和2.x版本也可以提供帮助。
答案 1 :(得分:0)
为了完整性:我正在使用lines = file.readlines()读取文件并使用标准print()函数进行打印。两端都没有手动编码或解码。
在Python 3.x中,标准print
函数只将Unicode写入sys.stdout
。由于这是io.TextIOWrapper
,因此其write
方法与此相同:
self.wrapped_binary_file.write(s.encode(self.encoding, self.errors))
所以一个可能的问题是sys.stdout.encoding
与终端的实际编码不匹配。
当然另一个原因是你的shell的编码与终端窗口的编码不匹配。
例如,在OS X上,我创建了一个像这样的myscript.py:
print('\u00e5')
然后我启动Terminal.app,创建一个编码为“Western(ISO Latin 1)”的会话配置文件,创建一个包含该会话配置文件的选项卡,并执行以下操作:
$ export LANG=en_US.UTF-8
$ python3 myscript.py
......我得到了你所看到的行为。
答案 2 :(得分:0)
从您的评论中可以看出,您使用的是python-2而不是python-3。
如果您使用的是python-3,则值得阅读unicode howto guide上的reading/writing以了解python正在做什么。
编码的基本流程为:
从编码到unicode的DECODE - >处理 - >从unicode编码到编码
在python3中,字节解码到字符串,字符串编码到字节。
使用open()
处理字符串解码的字节。
[..]内置的 open()函数可以返回类似文件的对象 假设文件的内容采用指定的编码并接受 read()和write()等方法的Unicode参数。这有效 通过open()的编码和错误参数[..]
因此,要从 utf-8编码的文件中读取 unicode ,您应该这样做:
# python-3
with open('utf8.txt', mode='r', encoding='utf-8') as f:
lines = f.readlines() # returns unicode
如果您想使用python-2进行类似的功能,可以使用codecs.open()
:
# python-2
import codecs
with codecs.open('utf8.txt', mode='r', encoding='utf-8') as f:
lines = f.readlines() # returns unicode