2016-07-29 13 views
0

Während Python Migration 3, bemerkte ich einige Dateien, die wir den eingebauten in csv jetzt b' Präfix haben um jede Saite erzeugen mit ...schreiben Nicht-Unicode mit csv-Modul

Hier ist der Code, das sollte eine generieren CSV für eine Liste von dogs, nach einigen von export_fields definierten Parameter (also gibt immer Unicode-Daten):

file_content = StringIO() 
csv_writer = csv.writer(
    file_content, delimiter='\t', quotechar='"', quoting=csv.QUOTE_MINIMAL 
) 
csv_writer.writerow([ 
    header_name.encode('cp1252') for _v, header_name in export_fields 
]) 
# Write content 
for dog in dogs: 
    csv_writer.writerow([ 
     get_value(dog).encode('cp1252') for get_value, _header in export_fields 
    ]) 

Das Problem ist, wenn ich file_content.getvalue() zurückkehrt, erhalte ich:

b'Does he bark?' b'Full  Name' b'Gender' 
b'Sometimes, yes' b'Woofy the dog' b'Male' 

Statt (Vertiefung modifiziert wurde auf SO lesbar zu sein):

'Does he bark?' 'Full  Name' 'Gender' 
'Sometimes, yes' 'Woofy the dog' 'Male' 

ich keine encoding Parameter im csv Modul gefunden. Ich möchte, dass die ganze Datei in cp1252 kodiert wird, also ist es mir egal, ob die Codierung durch die Iteration der Zeilen oder die selbst erstellte Datei erfolgt.

Also weiß jemand, wie man eine richtige Zeichenfolge erzeugt, die nur cp1252-codierte Zeichenfolgen enthält?

+0

Warum sind Sie in erster Linie kodiert? Das offene Dateiobjekt kümmert sich darum. –

+0

@MartijnPieters Vielleicht ist meine Frage dann unvollständig: Ich möchte den String durch Django zurückgeben: 'return HttpResponse (generate_csv_file())'. Sollte ich stattdessen Kodierung auf Django-Ebene handhaben? –

+0

Siehe meine Antwort; Sie nähern sich dem auf der falschen Ebene; tabs und quotechars müssen ebenfalls codiert werden, aber dies ist die Aufgabe der I/O-Ebene, nicht das 'csv'-Modul oder der Code, der Zeilen erzeugt. –

Antwort

1

Die csv Modul befasst sich mit Text und wandelt alles, was mit str() keine Zeichenfolge in eine Zeichenfolge ist.

Nicht in bytes Objekte übergeben. Übergeben Sie str Objekte oder Typen, die sauber in Zeichenfolgen mit str() konvertieren. Das bedeutet, dass Sie keine Zeichenfolgen codieren sollten.

Wenn Sie cp1252 Ausgang benötigen, codieren die StringIO Wert:

file_content.getvalue().encode('cp1252') 

als StringIO Objekte auch nur in Text befassen.

noch besser, ein BytesIO object mit einem TextIOWrapper() verwenden für Sie die Codierung zu tun, wie das csv Modul an das Dateiobjekt schreibt:

from io import BytesIO, TextIOWrapper 

file_content = BytesIO() 
wrapper = TextIOWrapper(file_content, encoding='cp1252', line_buffering=True) 
csv_writer = csv.writer(
    wrapper, delimiter='\t', quotechar='"', quoting=csv.QUOTE_MINIMAL) 

# write rows 

result = file_content.getvalue() 

I Line-Pufferung auf der Verpackung aktiviert haben, so dass es‘ Wird bei jedem Schreiben einer Zeile automatisch an die BytesIO Instanz gesendet.

Jetzt produziert file_content.getvalue() Bytestring:

>>> from io import BytesIO, TextIOWrapper 
>>> import csv 
>>> file_content = BytesIO() 
>>> wrapper = TextIOWrapper(file_content, encoding='cp1252', line_buffering=True) 
>>> csv_writer = csv.writer(wrapper, delimiter='\t', quotechar='"', quoting=csv.QUOTE_MINIMAL) 
>>> csv_writer.writerow(['Does he bark?', 'Full  Name', 'Gender']) 
36 
>>> csv_writer.writerow(['Sometimes, yes', 'Woofy the dog', 'Male']) 
35 
>>> file_content.getvalue() 
b'Does he bark?\tFull  Name\tGender\r\nSometimes, yes\tWoofy the dog\tMale\r\n' 
+0

Sieht aus wie es mit dem Wrapper in der Tat funktioniert (einmal flushed, aber Sie haben die Bearbeitung gemacht, bevor ich die Zeit habe, einen Kommentar abzugeben).Tests bestanden zu 99% sicher, dass es die richtige Antwort ist :) –

+0

@MaximeLorant: Ich habe es jetzt auf Zeilenpufferung umgestellt; vermeidet das manuelle Spülen. Das tut mir leid. –

+0

Scheint in der Tat sauberer! Danke für den Tipp. –