web-dev-qa-db-fra.com

Lire et écrire des fichiers CSV, y compris unicode avec Python 2.7

Je suis nouveau sur Python, et j’ai une question sur la façon d’utiliser Python pour lire et écrire des fichiers CSV. Mon fichier contient des informations comme l’Allemagne, le Français, etc. Selon mon code, les fichiers peuvent être lu correctement en Python, mais lorsque je l’écris dans un nouveau fichier CSV, l’unicode devient des caractères étranges.

Les données sont comme: enter image description here

Et mon code est:

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()

Et le résultat est comme: enter image description here

Pourriez-vous me dire ce que je devrais faire pour résoudre le problème? Merci beaucoup!

62
Ruxuan Ouyang

Une autre alternative:

Utilisez le code du paquet 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

Ce module est compatible API avec le module csv STDLIB.

52
Oz123

Assurez-vous d’encoder et de décoder comme il convient.

Cet exemple retournera un exemple de texte dans utf-8 vers un fichier csv et reviendra pour montrer:

# -*- 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])

Impressions:

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

Il existe un exemple à la fin de documentation du module csv qui montre comment traiter Unicode. Ci-dessous est copié directement à partir de cela exemple . Notez que les chaînes lues ou écrites seront des chaînes Unicode. Ne passez pas une chaîne d'octets à UnicodeWriter.writerows, par exemple.

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)

Entrée (codée UTF-8):

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

Sortie:

"American","美国人"
"French","法国人"
"German","德国人"
30
Mark Tolonen

Parce que str dans python2 est bytes en fait. Donc, si vous voulez écrire unicode sur CSV, vous devez encoder unicode à str à l'aide de l'encodage utf-8.

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

Utilisez class csv.DictWriter(csvfile, fieldnames, restval='', extrasaction='raise', dialect='Excel', *args, **kwds):

  • py2
    • Le csvfile: open(fp, 'w')
    • clé de passe et valeur dans bytes qui sont codés avec utf-8
      • writer.writerow({py2_unicode_to_str(k): py2_unicode_to_str(v) for k,v in row.items()})
  • py3
    • Le csvfile: open(fp, 'w')
    • passe normal contient str comme row à writer.writerow(row)

Enfin code

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)

Conclusion

En python3, utilisez simplement l'unicode str.

En python2, utilisez unicode handle text, utilisez str lorsque des E/S se produisent.

3
weaming

Je ne pouvais pas répondre à Mark ci-dessus, mais je venais d'apporter une modification qui corrigeait l'erreur provoquée si les données des cellules n'étaient pas unicode, c'est-à-dire des données float ou int. J'ai remplacé cette ligne dans la fonction UnicodeWriter: "self.writer.writerow ([s.encode (" utf-8 ")) si type (s) == types.UnicodeType sinon pour s en rangée]) de sorte qu'il devienne :

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)

Vous devrez également "importer des types".

2
Joe S

J'ai eu le même problème. La réponse est que vous le faites déjà bien. C'est le problème de MS Excel. Essayez d’ouvrir le fichier avec un autre éditeur et vous remarquerez que votre encodage a déjà réussi. Pour rendre MS Excel heureux, passez de UTF-8 à UTF-16. Cela devrait fonctionner:

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)
2
tozCSS