Чтение и запись CSV файлов, включая unicode с Python 2.7
Я новичок в 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]()
Не могли бы вы рассказать мне, что я должен сделать, чтобы решить эту проблему? Большое вам спасибо!
Ответы
Ответ 1
Убедитесь, что вы кодируете и декодируете соответственно.
В этом примере мы рассмотрим пример текста в 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
Ответ 2
Другая альтернатива:
Используйте код из пакета 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
Этот модуль API совместим с модулем CSV STDLIB.
Ответ 3
В конце документации модуля CSV есть пример, демонстрирующий, как работать с Unicode. Ниже скопировано прямо из этого example. Обратите внимание, что строки, считанные или записанные, будут строками 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","德国人"
Ответ 4
У меня была такая же проблема. Ответ в том, что вы делаете это прямо сейчас. Это проблема MS Excel. Попробуйте открыть файл другим редактором, и вы заметите, что ваша кодировка прошла успешно. Чтобы сделать MS Excel счастливым, перейдите от 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)
Ответ 5
Я не мог ответить на Mark выше, но я только что сделал одну модификацию, которая исправила ошибку, которая была вызвана, если данные в ячейках не были unicode, то есть float или int data. Я заменил эту строку на функцию UnicodeWriter: "self.writer.writerow([s.encode(" utf-8 "), если type (s) == types.UnicodeType else s для s в строке])", чтобы оно стало
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)
Вам также потребуется "импортировать типы".