2013-06-21 2 views
50

Я новичок в Python, и у меня есть вопрос о том, как использовать Python для чтения и записи CSV-файлов. Мой файл содержит, как Германия, французский и т. Д. Согласно моему коду, файлы можно корректно читать на Python, но когда я пишу его в новый CSV-файл, unicode становится странным символом.Чтение и запись CSV-файлов, включая unicode с Python 2.7

данные, как: 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

могли бы вы сказать мне, что я должен сделать, чтобы решить проблема? Большое спасибо!

+0

вы можете закодировать его в base64, у pythn есть модуль для этого тоже. – PepperoniPizza

+3

Я использую этот https://github.com/jdunck/python-unicodecsv –

+0

Какую кодировку источника и назначения вы используете для своих файлов? Unicode не является кодировкой (если вы не Microsoft ... но они действительно означают UTF-16LE). –

ответ

28

В конце csv module documentation приведен пример, который демонстрирует, как работать с 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","德国人" 
+1

Я все еще получаю 'UnicodeDecodeError: 'ascii' кодек не может декодировать байт 0xc3 в позиции 1: порядковый не в диапазоне (128)' on' self.writer.writerow ([s.encode ("utf-8") для s в строке]) 'этой строки. Пожалуйста, предложите? – Ahsan

+1

@ Ахсан, эта строка кодируется, но ошибка 'UnicodeDecodeError'. Это означает, что 's' не был Unicode для начала, поэтому Python 2.X расшифровывает его в Unicode, используя кодек' ascii' по умолчанию. Убедитесь, что вы передаете строки Unicode в 'UnicodeWriter'. –

+0

Да, именно это и было причиной. Мне удалось решить эту проблему по этой ссылке. http://stackoverflow.com/a/22734072/534790 Спасибо! Можете ли вы, пожалуйста, обновить ответ, если кто-то другой столкнется с этой же проблемой? – Ahsan

39

Убедитесь, что вы кодирования и декодирования в зависимости от обстоятельств.

Этот пример Roundtrip некоторый пример текста в кодировке 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 
+1

В принципе, до тех пор, пока _everything_ кодируется как Unicode, он работает отлично. Спасибо, что запустили дом без огромной стены кода! – Subir

+0

Большое вам спасибо, это действительно помощь! Позвольте мне понять, насколько я понял, как это работает: даже если вы сохраняете свои строки на Python, как 'u'Straße'', они все еще (избегаются как) ASCII внутри (' u'Stra \ xdfe''), так что вам нужно перевести/закодировать все в UTF-8 (экранированные строки) ('' Stra \ xc3 \ x9fe''), прежде чем записывать их в кодированный файл UTF-8? – doncherry

+0

@doncherry: Нет, строки представлены внутри, поскольку они закодированы. Если вы видите их как экранированные ascii, то это представление в то время или способ, которым вы должны вводить их. – dawg

37

Другой вариант:

Используйте код из пакета 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.

+2

Спасибо! Это простой способ сделать это. – keybits

+0

unicodecsv был для меня –

2

У меня была такая же проблема. Ответ в том, что вы делаете это прямо сейчас. Это проблема 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) 
0

Я не мог ответить Марку выше, но я только что сделал одну модификацию, которая исправлена ​​ошибка, которая была вызвана, если данные в клетках не юникод, т.е. поплавок или ИНТ данных. Я заменил эту строку на функцию 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) 

Вам также потребуется «импортировать типы».