使用 Python 2.7读写 CSV 文件,包括 unicode

我是 Python 的新手,我有一个关于如何使用 Python 读写 CSV 文件的问题。我的档案里有德国,法国等等。根据我的代码,可以用 Python 正确地读取这些文件,但是当我将它们写入一个新的 CSV 文件时,unicode 会变成一些奇怪的字符。

数据是这样的:
enter image description here

我的原则是:

import csv


f=open('xxx.csv','rb')
reader=csv.reader(f)


wt=open('lll.csv','wb')
writer=csv.writer(wt,quoting=csv.QUOTE_ALL)


wt.close()
f.close()

结果就是:
enter image description here

我应该做什么来解决这个问题?

125525 次浏览

Csv 模块文档的末尾有一个示例演示如何处理 Unicode。下面是直接从该 例子复制。注意,读取或写入的字符串将是 Unicode 字符串。例如,不要将字节字符串传递给 UnicodeWriter.writerows

import csv,codecs,cStringIO


class UTF8Recoder:
def __init__(self, f, encoding):
self.reader = codecs.getreader(encoding)(f)
def __iter__(self):
return self
def next(self):
return self.reader.next().encode("utf-8")


class UnicodeReader:
def __init__(self, f, dialect=csv.excel, encoding="utf-8-sig", **kwds):
f = UTF8Recoder(f, encoding)
self.reader = csv.reader(f, dialect=dialect, **kwds)
def next(self):
'''next() -> unicode
This function reads and returns the next line as a Unicode string.
'''
row = self.reader.next()
return [unicode(s, "utf-8") for s in row]
def __iter__(self):
return self


class UnicodeWriter:
def __init__(self, f, dialect=csv.excel, encoding="utf-8-sig", **kwds):
self.queue = cStringIO.StringIO()
self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
self.stream = f
self.encoder = codecs.getincrementalencoder(encoding)()
def writerow(self, row):
'''writerow(unicode) -> None
This function takes a Unicode string and encodes it to the output.
'''
self.writer.writerow([s.encode("utf-8") for s in row])
data = self.queue.getvalue()
data = data.decode("utf-8")
data = self.encoder.encode(data)
self.stream.write(data)
self.queue.truncate(0)


def writerows(self, rows):
for row in rows:
self.writerow(row)


with open('xxx.csv','rb') as fin, open('lll.csv','wb') as fout:
reader = UnicodeReader(fin)
writer = UnicodeWriter(fout,quoting=csv.QUOTE_ALL)
for line in reader:
writer.writerow(line)

输入(UTF-8编码) :

American,美国人
French,法国人
German,德国人

产出:

"American","美国人"
"French","法国人"
"German","德国人"

确保适当地进行编码和解码。

这个例子将把 utf-8中的一些示例文本往返到一个 csv 文件,然后返回来演示:

# -*- coding: utf-8 -*-
import csv


tests={'German': [u'Straße',u'auslösen',u'zerstören'],
'French': [u'français',u'américaine',u'épais'],
'Chinese': [u'中國的',u'英語',u'美國人']}


with open('/tmp/utf.csv','w') as fout:
writer=csv.writer(fout)
writer.writerows([tests.keys()])
for row in zip(*tests.values()):
row=[s.encode('utf-8') for s in row]
writer.writerows([row])


with open('/tmp/utf.csv','r') as fin:
reader=csv.reader(fin)
for row in reader:
temp=list(row)
fmt=u'{:<15}'*len(temp)
print fmt.format(*[s.decode('utf-8') for s in temp])

印刷品:

German         Chinese        French
Straße         中國的            français
auslösen       英語             américaine
zerstören      美國人            épais

另一种选择:

使用 unicodecsv 包中的代码..。

Https://pypi.python.org/pypi/unicodecsv/

>>> import unicodecsv as csv
>>> from io import BytesIO
>>> f = BytesIO()
>>> w = csv.writer(f, encoding='utf-8')
>>> _ = w.writerow((u'é', u'ñ'))
>>> _ = f.seek(0)
>>> r = csv.reader(f, encoding='utf-8')
>>> next(r) == [u'é', u'ñ']
True

该模块与 STDLIB csv 模块兼容。

我也有同样的问题。答案是你已经做对了。这是微软 Excel 的问题。尝试用另一个编辑器打开该文件,您将注意到您的编码已经成功。要使 MSExcel 快乐,请从 UTF-8移动到 UTF-16。这应该会奏效:

class UnicodeWriter:
def __init__(self, f, dialect=csv.excel_tab, encoding="utf-16", **kwds):
# Redirect output to a queue
self.queue = StringIO.StringIO()
self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
self.stream = f


# Force BOM
if encoding=="utf-16":
import codecs
f.write(codecs.BOM_UTF16)


self.encoding = encoding


def writerow(self, row):
# Modified from original: now using unicode(s) to deal with e.g. ints
self.writer.writerow([unicode(s).encode("utf-8") for s in row])
# Fetch UTF-8 output from the queue ...
data = self.queue.getvalue()
data = data.decode("utf-8")
# ... and reencode it into the target encoding
data = data.encode(self.encoding)


# strip BOM
if self.encoding == "utf-16":
data = data[2:]


# write to the target stream
self.stream.write(data)
# empty queue
self.queue.truncate(0)


def writerows(self, rows):
for row in rows:
self.writerow(row)

我无法回应上面的 Mark,但是我做了一个修改,修正了如果单元格中的数据不是 unicode (比如 float 或 int 数据)引起的错误。我在 UnicodeWriter 函数中替换了这一行: “ self. writer.writerow ([ s.encode (“ utf-8”)) if type (s) = = types。UnicodeType else s for s in row ]) ,因此它变成:

class UnicodeWriter:
def __init__(self, f, dialect=csv.excel, encoding="utf-8-sig", **kwds):
self.queue = cStringIO.StringIO()
self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
self.stream = f
self.encoder = codecs.getincrementalencoder(encoding)()
def writerow(self, row):
'''writerow(unicode) -> None
This function takes a Unicode string and encodes it to the output.
'''
self.writer.writerow([s.encode("utf-8") if type(s)==types.UnicodeType else s for s in row])
data = self.queue.getvalue()
data = data.decode("utf-8")
data = self.encoder.encode(data)
self.stream.write(data)
self.queue.truncate(0)


def writerows(self, rows):
for row in rows:
self.writerow(row)

您还需要“导入类型”。

因为 python2中的 str实际上是 bytes。因此,如果要将 unicode写入 csv,则必须使用 utf-8编码将 unicode编码为 str

def py2_unicode_to_str(u):
# unicode is only exist in python2
assert isinstance(u, unicode)
return u.encode('utf-8')

使用 class csv.DictWriter(csvfile, fieldnames, restval='', extrasaction='raise', dialect='excel', *args, **kwds):

  • Py2
    • csvfile: open(fp, 'w')
    • 传递 bytes中用 utf-8编码的键和值
      • writer.writerow({py2_unicode_to_str(k): py2_unicode_to_str(v) for k,v in row.items()})
  • Py3
    • csvfile: open(fp, 'w')
    • str作为 row传递给 writer.writerow(row)

终于有密码了

import sys


is_py2 = sys.version_info[0] == 2


def py2_unicode_to_str(u):
# unicode is only exist in python2
assert isinstance(u, unicode)
return u.encode('utf-8')


with open('file.csv', 'w') as f:
if is_py2:
data = {u'Python中国': u'Python中国', u'Python中国2': u'Python中国2'}


# just one more line to handle this
data = {py2_unicode_to_str(k): py2_unicode_to_str(v) for k, v in data.items()}


fields = list(data[0])
writer = csv.DictWriter(f, fieldnames=fields)


for row in data:
writer.writerow(row)
else:
data = {'Python中国': 'Python中国', 'Python中国2': 'Python中国2'}


fields = list(data[0])
writer = csv.DictWriter(f, fieldnames=fields)


for row in data:
writer.writerow(row)

结论

在 python3中,只需使用 unicode str

在 python2中,使用 unicode句柄文本,在 I/O 发生时使用 str

我不认为这是最好的答案,但它可能是最自成一体的答案,也是最有趣的。

UTF7 是 Unicode 的7位 ASCII 编码。正好 UTF7没有特别使用逗号、引号或空格。它只是将它们从输入传递到输出。因此,如果先用 UTF7编码,然后解析为 CSV,或者先用 CSV 解析,然后用 UTF7编码,那么结果真的没有什么区别。Python2的 CSV 解析器不能处理 unicode,但是 python2有一个 UTF-7编码器。因此,您可以编码、解析然后解码,这就好像您拥有一个支持 unicode 的解析器。

import csv
import io


def read_csv(path):
with io.open(path, 'rt', encoding='utf8') as f:
lines = f.read().split("\r\n")
lines = [l.encode('utf7').decode('ascii') for l in lines]
reader = csv.reader(lines, dialect=csv.excel)
for row in reader:
yield [x.encode('ascii').decode('utf7') for x in row]


for row in read_csv("lol.csv"):
print(repr(row))


Lol.csv

foo,bar,foo∆bar,"foo,bar"

输出 :

[u'foo', u'bar', u'foo\u2206bar', u'foo,bar']