Aus csv-Format in ein benutzerdefiniertes Format umwandeln

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
Mawilo
User
Beiträge: 452
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Hallo Leute,

ich bin noch ein Neuling in Python und habe ein kleines Problem. Ich möchte Daten aus csv-Format in ein benutzerdefiniertes Format umwandeln.

Input (csv-Format):

Code: Alles auswählen

SProduct;IType;SName
Prod1;36;
Prod2;24;ZVON
Prod3;34;VVO
Prod4;3;ZVON
Der Output soll dann so aussehen:

Code: Alles auswählen

3		                    #Anzahl der Spalten#
SProduct,		            # Inhalt Spalte 1#
IType,		               # Inhalt Spalte 2#
SName,		               # Inhalt Spalte 3#
"Prod1",36,"",
"Prod2",24,"ZVON",
"Prod3",34,"VVO",
"Prod4",3,"ZVON",
Wenn der erste Buchstabe der Spaltenbeschriftung ein "S" (für String-Format) ist, muss der Inhalt der Spalte in " " gesetzt werden.
Mit dem csv-Modul in Python bin ich nicht richtig zurecht gekommen.

Kann mir jemand einen Ansatz liefern, wie ich das umsetzen kann?
Die Inputtabellen können im Inhalt und der Anzahl der Spalten und Zeilen unterschiedlich sein.
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Gast hat geschrieben:schau dir mal das modul cv an:

http://www.python.org/doc/current/lib/module-csv.html
Hi. Ich finde man sollte schon mal wenigstens eine Frage vollstädig lesen, bevor man eine Antwort hinklatscht... Stephan hat oben geschrieben:
Stephan hat geschrieben:Mit dem csv-Modul in Python bin ich nicht richtig zurecht gekommen.
ergo hat er sich das wohl schon angeschaut.

Aber ich glaube auch nicht, dass du mit dem csv - Modul weiterkommst. Soweit ich mir das mal angeschaut habe, ist es nicht möglich ein csv-Dialect so zu definieren, dass er dir das automatisch alles parst. In deinem Fall wäre es einfacher selbst Hand anzulegen, da Strings und Integerwerte nur im Header definiert werden... Also tuts ein einfaches split(';') um an die Werte zu kommen und eine kleine Analyse des Headers:

Code: Alles auswählen

f=open("datei.csv","r")
header=f.readline().replace('\n','').split(';')
analyse=(',').join([((i[0]=="I" and "%s") or ('"%s"')) for i in header])+','

print len(header)
for i in header:
    print i+","
for line in f:
    print analyse%tuple(line.replace('\n','').split(';'))
Benutzeravatar
Mawilo
User
Beiträge: 452
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Danke Milan, ich bin schon ziemlich begeistert :D .
Ich bekomme folgendes Resultat (allerdings mit einer Fehlermeldung :?) :

Code: Alles auswählen

3
SProduct,
IType,
SName,

Traceback (most recent call last):
  File "C:/.../csv2etab_ver3.py", line 9, in -toplevel-
    print analyse%tuple(line.replace('\n','').split(';'))
TypeError: int argument required
Hast Du dazu noch ne Idee?
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Ja: du warst ein wenig zu schnell im rauskopieren: kurz danach hab ich den Fehler bemerkt und korrigiert. Ich hab oben einmal "%i" statt "%s" geschrieben, weil ja ein Integer in der Datei gemeint ist. Der ist aber beim auslesen nach wie vor ein String (wurde ja noch nicht konvertiert), was den Fehler auslöst. Probiers jetzt noch mal...

Milan
Benutzeravatar
Mawilo
User
Beiträge: 452
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Du bist der Beste!! :D

In meinen vielen versuchen vorher hatte ich immer das vierfache an Code und es hat nie funktioniert.

Vielen Dank. Ich werde das ganze jetzt noch hübsch in ein GUI packen.
Gast

Hallo Milan,

ich habe noch eine kleine Bitte, damit ich nicht nur staunend den Code kopiere, sondern auch was lerne.

Kannst Du eine kurze Erklärung zur Zeile

Code: Alles auswählen

analyse=(',').join([((i[0]=="I" and "%s") or ('"%s"')) for i in header])+','  
und zur Zeile

Code: Alles auswählen

print analyse%tuple(line.replace('\n','').split(';'))
geben?

Vielen Dank
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Hi. Gerne... im Prinzip hab ich auch einen rel. langen Quelltext, hab aber mal in Python's Buildin-kiste gekramt. Also zur 1. Zeile: da hab ich ein join verwendet. join fügt eine Liste von Strings zusammen und zwar mit dem String, den ich angegeben hab (","). Also würde aus ["ha","Ha"] ein "ha,Ha" werden. Wie die Liste gebildet wird erklär ich gleich. Ganz am Ende steht noch ein +"," . Das ist das abschließende Komma, was nicht durch join gesetzt wird und deswegen so angefügt werden muss.
Und die Liste wird mit einer List Comprehenshion gebildet. Das ist eigentlich eine Art for Schleife in einer Zeile. für jedes i aus einer Sequenz soll irgendetwas gemacht werden, die Einzelergebnisse werden zu einer Liste zusammengefügt. also so in etwa von der Schreibweise: [f(x) for x in sequenz]

Damit du das ganze mal besser verdauen kannst, hier mal alles aufgedröselt:

Code: Alles auswählen

liste=[]
for i in header:
    if i[0]=="I":
        liste.append("%s")
    else:
        liste.append('"%s"')
analyse=(',').join(liste)
analyse=analyse+","
Wie du siehst ist das also eine for-Schleife und das "and und or" Konstrukt ist nur eine If-Abfrage. Das ganze geht durch einne Trick in der Auswertung von Ausdrücken mit and und or, nimms jetzt erst mal so hin.

Zur zweiten Zeile: hier wird der analyse String verarbeitet. Als Ergebniss von oben dürfte er in etwa so hier aussehen:

Code: Alles auswählen

>>> analyse
'"%s",%s,"%s"'
Hier nutze ich aus, dass man Strings auf schöne Art und Weise "formatieren" kann, indem man Sachen in den String einfügen lässt. Dazu schreibt man ein % und dahinter den Datentyp, der eingefügt wird. Aus der Tabelle vom Link kannst du ablesen, dass wenn man Strings einfügen will, ein %s schreiben muss. Danach gibt man hinter den String ein Tupel an, indem die Werte stehen, die einzufügen sind. Beispiel:

Code: Alles auswählen

>>> "In die Klammern kommt ein String: (%s)" % ("TestTest",)
'In die Klammern kommt ein String: (TestTest)'
Dafür hab ich einfach die frisch aus der Datei ausgelesenen Werte genommen. In line steckt eine Zeile der Datei inklusive Zeilensprung, dem "\n". Das soll weg, also das line.replace. Außerdem wollen wir die Werte nach den Semikolon getrennt haben, was split erledigt. Beispiel:

Code: Alles auswählen

>>> "aha;AHA".split(";")
['aha','AHA']
So, und schlussendlich brauch ich statt einer Liste aber nun ein Tupel, wie ich oben geschrieben hab. Also noch schnell ein Tuple drüber und fertig.

Zusammenfassung :wink: :lol: : erst erstell ich mir ein Muster, was Python sagt, wie was ausgegeben werden muss (in der 1. Zeile) und in der 2. setze ich die Werte aus der Datei in das Muster ein.

Ich hoffe, das war nicht zu dick aufgetragen oder zuviel Theorie auf einmal... Ich hab das so gedrängt geschrieben, weil ich damit zurecht komme, aber wenn man es lesbarer haben will ist das auch kein Problem.

hth, Milan
Benutzeravatar
Mawilo
User
Beiträge: 452
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Danke für die Ausführungen. Ich glaube, wenn ich mir das unter's Kopfkissen lege, habe ich das morgen auch alles verstanden :D .


Vielen Dank

Stephan
Benutzeravatar
Mawilo
User
Beiträge: 452
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Hallo,
Ich habe das Umformen jetzt mal mit einer großen Datei versucht. Leider lassen sich nur 16 Zeilen umformen.
Danach bekomme ich folgende Fehlermeldung:

Code: Alles auswählen

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python23\lib\lib-tk\Tkinter.py", line 1345, in __call__
    return self.func(*args)
  File "C:\...\Desktop\Test csv2etab\csv2etab_def.py", line 57, in umformen
    print analyse%tuple(line.replace('\n','').split(';'))
TypeError: not enough arguments for format string
Hast Du noch ne Idee für mich?
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Hi. Da muss in einer Zeile weniger stehen, als oben im Header angegeben. Kannst du die 1. und die Fehlerzeile mal posten? Dann dürfte an sich nicht schwer sein, da was zu machen...

Milan
Benutzeravatar
Mawilo
User
Beiträge: 452
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Hi Milan,

manchmal sieht man den Wald vor lauter Bäumen nicht ... :idea:

Die Felder der letzten Spalte sind Kommentarfelder - und die sind in meinem Beispiel leer. Im csv-Format werden dann eben keine ";" mehr gesetzt.
Hast Du eine Idee, wie man dem Fehler vorbeugen kann? Als Master kann immer die Kopfzeile gelten, da dort immer alle Felder belegt sind.

Stephan
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Hi Stephan,

da gibts einige Möglichkeiten.

Code: Alles auswählen

>>> test = "aha;AHA"
 >>> print (test.split(";")+[""]*4)[:4]
['aha', 'AHA', '', '']
test.split(";") splittet den String wieder an den ";" auf und gibt eine Liste mit 2 Strings zurück. An diese Liste wird mit +[""]*4 eine Liste mit in diesem Fall 4 Leerstrings angehängt, du kannst auch schreiben +["","","",""] und [:4] gibt die ersten 4 Einträge der Liste zurück.

Gruß

Dookie
Benutzeravatar
Mawilo
User
Beiträge: 452
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Hi,

ich habe festgestellt, dass in der csv-Datei immer in den ersten 15 Datenzeilen (Zeile 2 bis 16)ein Semikolon als letztes Zeichen steht. ab der 16. Datenzeile (also ab Zeile 17) stehen keine Semikolons mehr. Lösche ich die ersten Zeilen, so hat das keine Auswirkungen auf das csv-Format. Es haben wieder nur die ersten 15 Datenzeilen ein Semikolon. :? Mit meinen begrenzten Kenntnissen in Python habe ich den Code so abgeändert:

Code: Alles auswählen

print len(header) 
for i in header: 
    print i+"," 
for line in f:
    anz = line.count(";")
    if anz + 2 == len(header):
        print analyse%tuple(line.replace('\n','').split(';')+[""])
    else:
        print analyse%tuple(line.replace('\n','').split(';'))
Das ist bestimmt nicht die schönste Lösung aber sie scheint zu funktionieren. :D

Vielen Dank

Stephan
Antworten