Dateinamen in jede Zeile schreiben

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.
BlackJack

@kidchino: Das mit den Leerzeilen wird wahrscheinlich daran liegen das Windows Textdateien anders behandelt als Binärdaten. Für das `csv`-Modul solltest Du die Dateien im Binärmodus öffnen damit Windows die Daten beim lesen/schreiben nicht verändert. Zum lesen ist das 'rb' und zum schreiben 'wb'.
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

@BlackJack:
BlackJack hat geschrieben:Zum lesen ist das 'rb' und zum schreiben 'wb'
Das habe ich probiert, aber dann kommt der Fehler:

Code: Alles auswählen

File "C:\Python34\test.py", line 16, in <module>
    next(reader) #überspringt jeweils die erste zeile #hier kommt ein fehler
_csv.Error: iterator should return strings, not bytes (did you open the file in text mode?)
Ich öffne aber sowohl Datei zum Lesen als auch zum Schreiben in/ oder mit "wb" bzw. "rb"

Code: Alles auswählen

gesamte_datei = open('C:/papererweiterung1/NeueNamenListe.csv', "wb")  #öffne die gesamte Datei
for dateiname in liste_dateiname:   # für jedes Element der Variable path - mach folgendes
        
    with open(os.path.join(pfad, dateiname), "rb") as f: #öffnet die jeweilige datei
Wenn ich "wt" und "rt" nutze komme kein Fehler aber die "Leerzeilen bleiben erhalten. "CR" am Ende jeder Zeile und dann folgend "CR""LF".
Das Format ist auch wieder "UTF-8 w/o BOM".
Hast du da noch ne idee?
Kann man das Format der Ursprungsdatei evtl. einlesen und sagen, dass die write-Datei das gleiche Format haben soll?

VG
kid
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Gib beim Öffnen der Datei noch das Encoding mit an (encoding='UTF-8').

Mich irritiert zudem, dass dein Skript offensichtlich im Python-Programmfolder liegt. Da gehört es nicht hin! Finger weg von diesem Ordner und seinen Unterordnern!
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

@ /me. Danke für den Tipp.
/me hat geschrieben:(encoding='UTF-8').!
Aber auch das funktioniert leider nicht.
Ich bekomme diese blöden Leerzeilen nicht weg.
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

Kann ein Grund sein, dass in den Input-Dateien teilweise Zellen leer sind, also Tabstop aufeinanderfolgen? Oder dass die einzelnen Elemente auch Sonderzeichen (bspw. ;[]#) enthalten?
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@kidchino: Kannst Du mal ein Beispiel zeigen? Wie sieht liste aus und was wird davon geschrieben, bzw. nicht geschrieben?
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

@Sirius3:
Hey,

ich bin mir nicht so ganz sicher wie ich es gut darstellen kann.
Hier ist erstmal eine rar mit den drei Input Datei und der einen Output Datei. Alles gekürzt und nur ein paar byte klein.
https://dl.dropboxusercontent.com/u/727 ... terung.rar

Falls ihr es nicht öffnen wollt, ist hier ein Screenshot, der natürlich nur halb gut die Formatierung widergibt.
https://dl.dropboxusercontent.com/u/7277609/test.jpg

Wenn ich die Beispiele irgendwie anders zeigen soll oder kann, sagt einfach bescheid.

Ich wäre Euch so dankbar wenn es klappt. Ich arbeite seit über zwei Jahren an diesen Daten und jetzt kommen endlich die letzten Schritte.

VG
Alex
BlackJack

Wenn ich das unter Linux laufen lasse funktioniert es. Das scheint eindeutig das Problem zu sein das Windows Textdateien nicht 1:1 so speichert wie man sie schreibt, sondern die Zeilenende-Zeichen ”übersetzt”. Um das zu umgehen würde man die Datei im Binärmodus öffnen. Was aber bei Python 3 anscheinend nicht mehr geht weil das `csv`-Modul dort jetzt eine Textdatei verlangt. :(
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

@BlackJack:

Wenn ich Python2 installieren und nutzen würde, müsste ich dann viel am Skript ändern. Ich bin ja kein Spezi (habt ihr ja gemerkt ;) ), der das in 2 Min hinbekommt. Oder bleiben die Methoden-Befehle etc. die gleichen?
Kann ich Python2 und Python3 auch parallel laufen lassen?

Vielen Dank für Eure Hilfe
Alex
BlackJack

@kidchino: Ich habe es unter Linux gerade mal ausprobiert, einzige Änderung war der zusätzliche Binärmodus bei den beiden `open()`-Aufrufen.
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

@BlackJack:

1000 Dank. Es läuft alles perfekt mit python 2.78. Viel Dank für die ganze Mühe und Hilfe.
Aber auch an Sirius3 und /me: Vielen Dank.

Vielleicht hilft diese Erkenntnis ja auch einem Dritten.
BlackJack hat geschrieben:Das scheint eindeutig das Problem zu sein das Windows Textdateien nicht 1:1 so speichert wie man sie schreibt, sondern die Zeilenende-Zeichen ”übersetzt”. Um das zu umgehen würde man die Datei im Binärmodus öffnen. Was aber bei Python 3 anscheinend nicht mehr geht weil das `csv`-Modul dort jetzt eine Textdatei verlangt.
VG
kid
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

Hi zusammen,

ich leider schon wieder.

Leider kommt ein Fehler wenn ich ein paar mehr Dateien nutze:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Python_Skripte\paper_add_name.py", line 20, in <module>
    for row in reader:         #schleife für jede zeile
Error: field larger than field limit (131072)
Die csv Datei wird auch relativ schnell groß. Nach 40 Dateien ist sie schon 16MB groß. (Ist ja aber kein Problem, nur zur Info)
Nun habe ich ca. 1000 Dateien im ersten Durchlauf.

Ich bin gar nicht sicher, was hier zu klein ist. Kann man das Limit ggf. erhöhen?

Hier nochmal das gesamte Skript:

Code: Alles auswählen

# -*- coding: utf-8 -*-
#Das Skript schreibt alle Zeilen aus allen Dateien eines Ordner in eine großes Datei und hängt den Datennamen hinten dran (4 Zellen)
import csv, os, sys

# relevanter Pfad mit Dateien
pfad = 'C:/papererweiterung/'

# Pfad zu den txt/csv Dateien 'Pfad'
liste_dateiname = os.listdir(pfad)

liste = [] #erstellt eine leeres Array
gesamte_datei = open('C:/papererweiterung1/New400Liste.csv', "wb")  #öffne die gesamte Datei
for dateiname in liste_dateiname:   # für jedes Element der Variable path 
        
    with open(os.path.join(pfad, dateiname), "rb") as f: #öffnet die jeweilige datei
        reader = csv.reader(f, delimiter="\t") #liest die zeilen aus, tabstop getrennt
        next(reader) #überspringt jeweils die erste zeile 
        #new_datei = open(os.path.join(pfad, dateiname) + "_new","w")
        #print (liste) #testausgabe        
        for row in reader:         #schleife für jede zeile   
            row.append(dateiname)#schreibt in jede zeile hinten den vollen dateinamen dran
            #del row[58]
            compid, nummer, _, subname = dateiname.split("#") #"splittet" den namen mit der Trennung "#"
            row.extend([compid, nummer, subname])#setzt den geteilten Dateinamen in die letzten drei Spalten
            liste.append(row) #schreibt die zeilen in die liste
writer = csv.writer(gesamte_datei,  delimiter = "\t")#gibt die liste weiter an die gesamte datei
writer.writerows(liste)#Zgibt die liste weiter an die gesamte datei


VG Kid
BlackJack

@kidchino: Dann müsstest Du Dir die Datei bei der der Fehler auftritt mal anschauen warum da ein Feld so gross ist. Kann es vielleicht sein, dass nicht alle Dateien das gleiche Trennzeichen verwenden? Oder das auch andere Dateien als CSV-Dateien in dem Verzeichnis liegen? Bei Windows tauchen ja zum Beispiel gerne mal so `Thumbs.db`-Dateien überall auf.

Bezüglich der Dateigrösse(n): Dann würde ich nicht alles in den Arbeitsspeicher lesen sondern statt die Zeilen in einer Liste zu sammeln, sie gleich in die Ausgabedatei schreiben.
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

Noch ein paar Infos:

Bei 340 Dateien mit insgesamt 55,6 MB läuft alles gut. Dann kommt eine ca. ebenso große CSV raus. Alles bestens
Die 341. Datei ist eine 1,4Mb große Datei. Nehme ich die dazu kommt der Fehler.
Lass ich aber die 341. aus und nehme die zwei darauffolgenden dazu (2x 900kb = 1.8mb) läuft das Skirpt wieder gut. Jetzt dachte ich, dass die Dateigröße der einzelnen Datei evtl. entscheidend ist, aber
wenn ich dann noch ein paar Dateien dazu nehmen, die kleiner sind als 1mb, kommt der Fehler trotzdem wieder.

Update:
@BlackJack:
Da ist tatsächlich eine Zeile in der 341. Datei dabei, die allein 154252 Zeichen hat. Kann man das umgehen bzw. das Limit erhöhen oder muss ich die Zeile löschen.
Wenn man es nicht umgehen könnte, wäre es ideal, dass das Skripte mir die Datei "printed", bei der der Fehler auftritt. Dann könnte ich die Dateien per Hand nachbearbeiten.
kidchino hat geschrieben:Bezüglich der Dateigrösse(n): Dann würde ich nicht alles in den Arbeitsspeicher lesen sondern statt die Zeilen in einer Liste zu sammeln, sie gleich in die Ausgabedatei schreiben.
Geht es denn wenn ich alle Inhalte in eine Datei kopieren?
Ich muss nachher alle Daten in eine MySQL-Datenbank laden und es ist für mich sehr viel einfacher, 10 große Dateien einzulesen also 20.000 kleine.


VG
kid
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

Hey,

ich habe jetzt beim CSV.reader

Code: Alles auswählen

quoting=csv.QUOTE_NONE
hinzugefügt.
Könnte das Probleme nachher bei MySQL geben? Sonst würde ich diese Extremfälle doch lieber rausnehmen und per Hand bearbeiten.
BlackJack

@kidchino: Das das Problem nicht die gesamtdateigrösse ist sagt die Fehlermeldung doch eigentlich, da steht „field size limit” und nicht „file size limit”. ;-)

Es gibt im `csv`-Modul die `field_size_limit()`-Funktion um dieses Limit zu setzen (und abzufragen).

Die Nachfrage verstehe ich nicht‽ Im Moment kopierst Du doch alles in eine Ausgabedatei. Ob das geht, also bei der Weiterverarbeitung, musst Du doch wissen‽

Im moment sammelst Du alle Daten in einer Liste und schreibst die in eine Ausgabedatei wenn die Schleife durchlaufen und alle Eingabedateien eingelesen sind. Stattdessen könntest Du dort wo Du einen Datensatz an die Liste anhängst diesen Datensatz gleich in die Ausgabedatei schreiben und die Liste komplett weg lassen. Damit beschränkt sich der benötigte Arbeitsspeicher auf den Platz der für die Verarbeitung des grössten Einzeldatensatzes gebraucht wird, und nicht mehr auf die Grösse die alle Datensätze in allen Dateien zusammen verbrauchen.

Ob Optionen beim *reader* Probleme beim importieren in die Datenbank machen, kann Dir hier keiner sagen. Dazu müsste man ja wissen wie die Daten aussehen, was die Optionen dabei bewirken, und was die Datenbank erwartet.
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

@BlackJack:
BlackJack hat geschrieben:@kidchino: Das das Problem nicht die gesamtdateigrösse ist sagt die Fehlermeldung doch eigentlich, da steht „field size limit” und nicht „file size limit”. ;-)
Haha, stimmt :)
BlackJack hat geschrieben:Es gibt im `csv`-Modul die `field_size_limit()`-Funktion um dieses Limit zu setzen (und abzufragen).
Okay, das probiere ich gleich mal aus.
BlackJack hat geschrieben:Die Nachfrage verstehe ich nicht‽ Im Moment kopierst Du doch alles in eine Ausgabedatei. Ob das geht, also bei der Weiterverarbeitung, musst Du doch wissen‽
BlackJack hat geschrieben:Ob Optionen beim *reader* Probleme beim importieren in die Datenbank machen, kann Dir hier keiner sagen. Dazu müsste man ja wissen wie die Daten aussehen, was die Optionen dabei bewirken, und was die Datenbank erwartet.
Meine Frage war eigentlich diese. Wenn ich jetzt die "zu großen fields" in der Datei lasse, dann ergebe sich doch bestimmt auch Probleme bei MySQL. Excel kann damit nicht um, das CSV-Modul meckert auch. Da dachte ich es ist naheliegend, dass auch MySQL Probleme damit hat wenn die Felder >x sind. Formatiert ist die MySQL Tabelle noch nicht, also könnte ich einfach eine Feldgröße vonr >x zulassen, falls x nicht ein Standard-Wert ist der und bspw. MySQL auf gar keinen Fall mit Felder umgehen kann, die >x sind.
BlackJack hat geschrieben:Im moment sammelst Du alle Daten in einer Liste und schreibst die in eine Ausgabedatei wenn die Schleife durchlaufen und alle Eingabedateien eingelesen sind. Stattdessen könntest Du dort wo Du einen Datensatz an die Liste anhängst diesen Datensatz gleich in die Ausgabedatei schreiben und die Liste komplett weg lassen. Damit beschränkt sich der benötigte Arbeitsspeicher auf den Platz der für die Verarbeitung des grössten Einzeldatensatzes gebraucht wird, und nicht mehr auf die Grösse die alle Datensätze in allen Dateien zusammen verbrauchen.
Der Ansatz ist nachvollziehbar und das dachte ich auch schon mal. Aber es ist ja immer nicht so einfach, wenn man bis vor einem Monat noch keinen Kontakt mit Programmiersprachen (außer Excel Makro und das ist ja weit entfernt) hatte. Aber ich werde es gleich mal probieren.

Ich versuche es ja mal so zu programmieren, wenn eine Feld zu groß ist, die Datei übersprungen und der Dateiname ausgegeben wird. Das würde ich auf Nummer Sicher gehen und müsste halt ein bisschen per Hand machen. Leider weiß ich nicht wie ich die Feldgröße ermitteln kann. Es müsste ja so in die Richtung gehen len(row).

VG
BlackJack

@kidchino: Ob sich mit zu grossen Feldern Probleme bei MySQL ergeben hängt von zwei Faktoren ab: 1. hat das Programm das zum Importieren der CSV-Datei in die Datenbank verwendet wird eventuell eine Grössenbeschränkung für einzelne Felder, und 2. ist der Datentyp in der entsprechenden Spalte geeignet um den grossen Wert (komplett) aufzunehmen. Hier ist auch noch die Frage interessant was beim Import mit Feldern passiert die grössere Daten enthalten als der Datentyp der Spalte erlaubt — führt das zu einem Fehler und einem Abbruch des Imports, oder gibt es eine Warnung. In letzterem Fall: Was passiert mit dem Datensatz? Wird der komplett ignoriert, oder wird der Überhang der zu grossen Felder einfach ”abgeschnitten”? Die Fragen solltest Du klären und entscheiden wie Du mit diesen Fällen umgehen möchtest.

Für Textfelder ist der grösste MySQL-Typ soweit ich weiss LONGTEXT und der kann bis zu 2 Gigabyte gross werden. Ein Feld vom Typ TEXT kann maximal 64 Kilobyte aufnehmen.

Kannst Du die maximale Feldgrösse nicht sinnvoll abschätzen? Was sind denn das für Daten? Kann da zum Beispiel tatsächlich ein Feld mit mehr als einem Megabyte an Inhalt vorkommen oder wäre das eine Grösse die man einfach mal setzen kann?

Um die maximale Feldgrösse zu bestimmen müsste man die CSV-Datei parsen, aber um das zu machen muss man die maximale Feldgrösse kennen. ;-)

Man könnte bei Dateien die zu grosse Felder enthalten die maximale Feldgrösse vom `csv`-Modul solange schrittweise erhöhen bis sich die Datei verarbeiten lässt um die maximale Feldgrösse zu ermitteln. Aber wie gesagt, es sollte möglich sein einen sinnvollen Wert zu schätzen wenn man weiss was das für Daten sind. Wie gross ist denn die grösste Einzeldatei?
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

BlackJack hat geschrieben:@kidchino: [...] Die Fragen solltest Du klären und entscheiden wie Du mit diesen Fällen umgehen möchtest.
Ich habe mich entschieden, diese Dateien erstmal außer vor zu lassen.
BlackJack hat geschrieben:Kannst Du die maximale Feldgrösse nicht sinnvoll abschätzen? Was sind denn das für Daten? Kann da zum Beispiel tatsächlich ein Feld mit mehr als einem Megabyte an Inhalt vorkommen oder wäre das eine Grösse die man einfach mal setzen kann?
BlackJack hat geschrieben:Man könnte bei Dateien die zu grosse Felder enthalten die maximale Feldgrösse vom `csv`-Modul solange schrittweise erhöhen bis sich die Datei verarbeiten lässt um die maximale Feldgrösse zu ermitteln. Aber wie gesagt, es sollte möglich sein einen sinnvollen Wert zu schätzen wenn man weiss was das für Daten sind. Wie gross ist denn die grösste Einzeldatei?
Das habe ich schrittweise gemacht. Liegt bei ca. 245000-247000 aber ich konnte noch nicht alle Daten prüfen. Die größte Datei ist 1.495kb groß.

Ist denn das "field" ein Element einer Zeile oder die gesamte Zeile an sich?

Hiermit:

Code: Alles auswählen

print(len(row[22]),dateiname)
habe ich geschaut wie viel in einer bestimmten Zelle ist. Hier ist es teilweise bei 108000 für eine einzelne Zelle.
Kann man nicht einfach prüfen und dann ggf. die Datein überspringen:

Code: Alles auswählen

for row in reader:         #schleife für jede zeile
            if summe(len(row[0])...len(row[63])) < 130000: #das in richtig
                row.append(dateiname)#schreibt in jede zeile hinten den vollen dateinamen dran
                compid, nummer, _, subname = dateiname.split("#") #"splittet" den namen mit der Trennung "#"
                row.extend([compid, nummer, subname])#setzt den geteilten Dateinamen in die letzten drei Spalten
                liste.append(row) #schreibt die zeilen in die liste
            else:
            gehe zu row+1 # und #das in richtig
            print(dateiname)
BlackJack hat geschrieben:Was sind denn das für Daten?
Das sind Informationen zu wissenschaftlichen Arbeiten. Das konkrete Problem (mit den großen Zellen) ist, dass es wissenschaftliche Publikationen (scheinbar im Medizinbereich) gibt wo ca. 400 Autoren angegeben sind. Und jeder hat eine Adresse und einen Namen einer Uni dabei. Da wird eine Zelle recht schnell voll. Ob der Inhalt einer Publikation mit 400 Autoren sinnvoll ist, steht wohl auch einem anderen Blatt ;)


VG kid
BlackJack

@kidchino: Wenn die grösste Datei 1,5 MiB gross ist dann setz das Feldlimit einfach auf 2 MiB. Grösser als die grösste Datei kann ein Feld ja unmöglich sein. :-)

„field” ist ein Element einer CSV-Zeile. (Eine CSV-Zeile muss sich nicht auf eine Zeile in der Textdatei beschränken, denn man kann auch Zeilenumbrüche innerhalb von Zellen haben.)

Um ein ``len(row[index])`` machen zu können muss `row` ja gelesen worden sein, dafür darf kein Feld grösser als die eingestellte „field size” sein.

Wenn die Ausnahme aufgetreten ist, dann kann man die Folgedatensätze aus der Datei nicht mehr verarbeiten würde ich mal behaupten. Man kann dann also nur die Datei, oder den Rest davon ignorieren. Dafür behandelt man halt die Ausnahme entsprechend.
Antworten