[更新]感谢答案和输入,但工作代码将是最受欢迎的。如果你能提供可以读取样本文件的代码,你就是王(或女王)。
[更新2]感谢您的出色答案和讨论。我需要做的就是读取它们,解析它们,并将它们的一部分保存在Django模型实例中。我相信这意味着将它们从原生编码转换为unicode,因此Django可以处理它们,对吗?
Stackoverflow上有several questions已经在非ascii python CSV读取的主题上,但是那里和python文档中显示的解决方案不适用于输入文件我是尝试。
解决方案的要点似乎是编码('utf-8')CSV读取器的输入和unicode(item,'utf-8')读取器的输出。但是,这会遇到UnicodeDecodeError问题(参见上面的问题):
UnicodeDecodeError: 'utf8' codec can't decode byte 0xa3 in position 8: unexpected
输入文件不一定是utf8;它可以是ISO-8859-1,cp1251,或其他任何东西。
那么,问题是:在Python中读取CSV文件的弹性,交叉编码能力是什么?
问题的根源似乎是CSV模块是C扩展;有一个纯python CSV阅读模块吗?
如果没有,有没有办法自信地检测输入文件的编码,以便可以处理它?</ p>
基本上我正在寻找一种防弹方式来读取(并希望写入)任何编码的CSV文件。
这是推荐的解决方案失败:
Python 2.6.4 (r264:75821M, Oct 27 2009, 19:48:32)
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import csv
>>> def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
... # csv.py doesn't do Unicode; encode temporarily as UTF-8:
... csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
... dialect=dialect, **kwargs)
... for row in csv_reader:
... # decode UTF-8 back to Unicode, cell by cell:
... yield [unicode(cell, 'utf-8') for cell in row]
...
>>> def utf_8_encoder(unicode_csv_data):
... for line in unicode_csv_data:
... yield line.encode('utf-8')
...
>>> r = unicode_csv_reader(file('sample-euro.csv').read().split('\n'))
>>> line = r.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in unicode_csv_reader
File "<stdin>", line 3, in utf_8_encoder
UnicodeDecodeError: 'ascii' codec can't decode byte 0xf8 in position 14: ordinal not in range(128)
>>> r = unicode_csv_reader(file('sample-russian.csv').read().split('\n'))
>>> line = r.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in unicode_csv_reader
File "<stdin>", line 3, in utf_8_encoder
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 28: ordinal not in range(128)
答案 0 :(得分:16)
您正在尝试将解决方案应用于不同的问题。请注意:
def utf_8_encoder( unicode_csv_data )
你正在喂它str
个物品。
读取非ASCII CSV文件的问题是您不知道编码而您不知道分隔符。如果您确实知道编码(并且它是基于ASCII的编码(例如cp125x,任何东亚编码,UTF-8,不 UTF-16,不 UTF-32 ))和分隔符,这将起作用:
for row in csv.reader("foo.csv", delimiter=known_delimiter):
row = [item.decode(encoding) for item in row]
您的sample_euro.csv看起来像带有逗号分隔符的cp1252。俄语的一个看起来像带有分号分隔符的cp1251。顺便说一下,从内容来看,您还需要确定使用的日期格式以及货币也可能 - 俄罗斯示例中有金额后跟空格和西里尔语“卢布”缩写。
请注意:拒绝所有说服您拥有ISO-8859-1编码文件的尝试。它们以cp1252编码。
更新以回应评论“”“如果我理解你所说的话我必须知道编码才能使其正常工作?在一般情况下,我不知道编码和基于另一个答案猜测编码是非常困难的,所以我运气不好?“”“
您必须知道 ANY 文件阅读练习的编码才能正常工作。
在任何大小的文件中,任何编码都能正确地猜测编码并不是很困难 - 这是不可能的。但是,将范围限制为以用户的语言环境的默认编码保存在Excel或Open Office之外的csv文件,且大小合理,这不是一项大任务。我建议你试试chardet;它为您的欧元文件猜测windows-1252
,为您的俄语文件猜测windows-1251
- 鉴于它们的小尺寸,这是一项了不起的成就。
更新2 以回应“”<“> 工作代码将是最受欢迎的”“
工作代码(Python 2.x):
from chardet.universaldetector import UniversalDetector
chardet_detector = UniversalDetector()
def charset_detect(f, chunk_size=4096):
global chardet_detector
chardet_detector.reset()
while 1:
chunk = f.read(chunk_size)
if not chunk: break
chardet_detector.feed(chunk)
if chardet_detector.done: break
chardet_detector.close()
return chardet_detector.result
# Exercise for the reader: replace the above with a class
import csv
import sys
from pprint import pprint
pathname = sys.argv[1]
delim = sys.argv[2] # allegedly known
print "delim=%r pathname=%r" % (delim, pathname)
with open(pathname, 'rb') as f:
cd_result = charset_detect(f)
encoding = cd_result['encoding']
confidence = cd_result['confidence']
print "chardet: encoding=%s confidence=%.3f" % (encoding, confidence)
# insert actions contingent on encoding and confidence here
f.seek(0)
csv_reader = csv.reader(f, delimiter=delim)
for bytes_row in csv_reader:
unicode_row = [x.decode(encoding) for x in bytes_row]
pprint(unicode_row)
输出1:
delim=',' pathname='sample-euro.csv'
chardet: encoding=windows-1252 confidence=0.500
[u'31-01-11',
u'Overf\xf8rsel utland',
u'UTLBET; ID 9710032001647082',
u'1990.00',
u'']
[u'31-01-11',
u'Overf\xf8ring',
u'OVERF\xd8RING MELLOM EGNE KONTI',
u'5750.00',
u';']
输出2:
delim=';' pathname='sample-russian.csv'
chardet: encoding=windows-1251 confidence=0.602
[u'-',
u'04.02.2011 23:20',
u'300,00\xa0\u0440\u0443\u0431.',
u'',
u'\u041c\u0422\u0421',
u'']
[u'-',
u'04.02.2011 23:15',
u'450,00\xa0\u0440\u0443\u0431.',
u'',
u'\u041e\u043f\u043b\u0430\u0442\u0430 Interzet',
u'']
[u'-',
u'13.01.2011 02:05',
u'100,00\xa0\u0440\u0443\u0431.',
u'',
u'\u041c\u0422\u0421 kolombina',
u'']
更新3 这些文件的来源是什么?如果它们从Excel或OpenOffice Calc或Gnumeric“保存为CSV”,则可以将它们保存为“Excel 97-2003 Workbook(* .xls)”并使用xlrd来读取整个编码剧他们。这也可以省去必须检查每个csv文件以确定分隔符(逗号与分号),日期格式(31-01-11 vs 04.02.2011)和“小数点”(5750.00 vs 450,00)的麻烦 - - 所有这些差异可能是通过保存为CSV 创建的。 [Dis] claimer:我是xlrd
的作者。
答案 1 :(得分:2)
我不知道你是否已经尝试过这个,但是在csv模块官方Python文档的example部分,你会找到一对类; UnicodeReader
和UnicodeWriter
。到目前为止,他们对我很好。
正确检测文件的编码似乎是一个非常难的问题。您可以阅读this StackOverflow thread中的讨论。
答案 2 :(得分:0)
通过尝试.encode('utf-8')
,你在代码中做错了,你应该解码它。顺便说一下,unicode(bytestr, 'utf-8')
== bytestr.decode('utf-8')
但最重要的是,为什么要尝试解码字符串?
听起来有点荒谬,但你可以使用这些CSV而不关心它们是cp1251,cp1252还是utf-8。这一切的美妙之处在于区域字符也是> 0x7F和utf-8,使用> 0x7F字符的序列来表示非ASCII符号。
由于CSV关注的分隔符(是,或;或\ n)在ASCII内,因此其工作不会受到所使用的编码的影响(只要它是一个字节或utf-8!)。
需要注意的重要一点是,您应该提供以csv
模式打开的Python 2.x binary
模块文件 - 即'rb'或'wb' - 因为它的特殊方式实施了。
答案 3 :(得分:0)
你问的是不可能的。没有办法用任何接受未知编码输入的语言编写程序,并正确地将其转换为Unicode内部表示。
您必须找到一种方法来告诉应用程序使用哪种编码。
可以识别许多但不是全部的编码,但它实际上取决于文件的内容是什么以及是否有足够的数据点。这类似于正确解码网络服务器上的文件名的问题。当在网络服务器上创建文件时,无法告诉服务器使用了什么编码,因此如果您有一个包含多个编码名称的文件夹,那么对于某些(如果不是全部)用户而言,它们将保证看起来很奇怪文件似乎很奇怪。
然而,不要放弃。试试这个问题中提到的chardet编码检测器:https://serverfault.com/questions/82821/how-to-tell-the-language-encoding-of-a-filename-on-linux 如果你很幸运,你不会失败很多。