我有以下代码可以读取csv文件(有些包含非UTF8字符)。它在Python 2.7.x中运行良好:
encodings = {'ukprocessed.csv': 'utf8',
'usprocessed.csv': 'utf8',
'uyprocessed.csv': 'latin1',
'arprocessed.csv': 'latin1'}
with codecs.open(filepath, 'r') as csvfile:
reader = csv.reader(csvfile)
for row in reader:
row = [x.decode(encodings[filename]).encode('utf8') for x in row]
但是,在Python 3.4.x中,测试失败并产生各种错误:
我已经在打开的文件中指定了'encoding =',用'rb'作为字节以其他方式打开,但我找不到在Python 2和3中都可以使用的解决方案。
有人对我该如何解决有任何想法吗?
谢谢
在Py3中,x
每个值row
都是str
(类似于Py2 unicode
)。在Py2中,str
它unicode
太灵活了,因为str
它既是文本数据类型又是二进制数据类型。它encode
通过假设str
是ASCII来支持某种解码,然后将其重新编码为选定的编码(对于ASCII兼容编解码器而言,这毫无意义,因为遇到非ASCII时会出错)。对于对称性,允许类似的容易出错且类型毫无意义decode
的错误unicode
;它将转换encode
为ASCII(如果unicode
包含非ASCII,则会出错),然后decode
在请求的编解码器中。这是各种误解,错误等的根源。
在Python 3中,他们可以更好地拆分类型:
str
是的文本类型,并且只具有一个encode
方法(从逻辑字符转换为一个特定的所述字符的二进制编码)bytes
(以及其他bytes
类似类型的数据)代表二进制数据,并且只有一种decode
方法(将特定的二进制编码转换为逻辑字符)您的代码要求支持“纯文本”类型decode
(binary-> text),并且正如我所指出的那样,Py2在有限的意义上允许这样做,即使它通常很笨。Py3不会;decode
-将逻辑文本转换为逻辑文本是荒谬的,并且为了避免无声的错误行为,Py3不提供无效的方法(Py2会根据unicode
对象的内容工作,然后在错误时失败;您会认为代码不是-英语友好,如果您将其与非英语文本一起实际使用,则会中断)。
csv
如果您需要完全的可移植性,那么编写必须处理非ASCII类型的代码并非易事。这是问题所在:
str
(面向字节的)编码方式,该编码方式不包含Embedded NUL
,而不是unicode
。注意: unicode
如果仅包含由返回的编码中的文本,则碰巧会起作用sys.getdefaultencoding()
,因为csv
通常使用ascii
在site
模块启动时使用值配置的值对文本进行静默编码sys.setdefaultencoding
。调用后site
删除sys.setdefaultencoding
,您不应该自己对其进行调整;当输入csv
从强制转换unicode
回时,输入内容不符合语言环境编码时,它会中断str
。这也不仅仅取决于您的系统区域设置。我的系统,使用上LANG=en_US.latin-1
或LANG=en_US.utf-8
,Python是回国还是'ascii'
我的sys.getdefaultencoding()
。str
(面向文本,相当于Py2的unicode
)通常,对于不csv
相关的情况,我建议使用纯粹基于文本的类型io.open
来获得Py2.7和Py3.x之间的完全兼容性(以及比更好的性能/兼容性codecs.open
)。但是io.open
(codecs.open
就此而言)在文本模式下会返回unicode
Py2(csv
除非可以使用默认编码表示,否则无法使用,因此您会认为它可以工作,直到您提供默认编码无法处理的内容为止),并且str
在Py3中(精细);在二进制模式下,它返回str
上的Py2(罚款,如果没有内嵌NUL
S,虽然它不是解码你,所以你需要两个解码来自str
于unicode
从,然后编码背unicode
到utf-8
str
),并bytes
在PY3(需要被解码str
) 。它很丑。
我能提供的最佳解决方案是使用io.open
,但在特定步骤产生的迭代器周围添加一个与版本相关的包装器,以确保该迭代器的输出具有给定Python版本(以Py2,Py3utf-8
编码)的适当形式。您保持一致的行为(并将版本检查限制为每个文件执行固定次数,而不是每行执行一次):str
str
import io
import sys
encodings = {'ukprocessed.csv': 'utf8',
'usprocessed.csv': 'utf8',
'uyprocessed.csv': 'latin1',
'arprocessed.csv': 'latin1'}
# io.open in text mode will return unicode on Py2, str on Py3, decoded appropriately
# newline='' prevents it from doing line ending conversions (which are csv's
# responsibility)
with io.open(filepath, encoding=encodings[filepath], newline='') as csvdata:
if sys.version_info[0] == 2:
# Lazily convert lines from unicode to utf-8 encoded str
csvdata = (line.encode('utf-8') for line in csvdata)
reader = csv.reader(csvdata)
if sys.version_info[0] == 2:
# Decode row values to unicode on Py2; they're already str in Py3
reader = ([x.decode('utf-8') for x in row] for row in reader)
for row in reader:
# operate on row containing native text types as values that can
# represent whole Unicode range (unicode on Py2, str on Py3)
...
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句