| 
| Autor | Beitrag |  
| CodingForBeer Hält's aus hier
 Beiträge: 7
 
 
 
 
 | 
Verfasst: Do 11.09.14 11:27 
 
Hallöle,
 mit folgendem Code lese ich eine CSV-Datei 
 ein und ersetze die Zeichenfolge ", durch ";
 		                       C#-Quelltext 
 									| 1:2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
 10:
 
 | StreamReader inputStreamReader3 = File.OpenText(filepathread);String Inhalt3 = inputStreamReader3.ReadToEnd();
 inputStreamReader3.Close();
 String ersetzen3 = "\",";
 String durch3 = "\";";
 Inhalt3 = Inhalt3.Replace(ersetzen3, durch3);
 StreamWriter outputStreamWriter3 = File.CreateText(filepathread);
 outputStreamWriter3.Write(Inhalt3);
 outputStreamWriter3.Close();
 Inhalt3 = "";
 |  Die CSV-Datei hat eine Größe von: 125,126 KB
 Excel kann sie nicht vollständig laden, da sie mehr als 1.048.576 Zeilen hat
 Der Windows-Editor kann sie laden. Da habe ich gesehen, dass die Datei 1.106.601 Zeilen hat.
 Die Spaltenanzahl beträgt: 11
 Folgende Fehlermeldung tritt beim Programmablauf auf:
 Ein Ausnahmefehler des Typs "System.OutOfMemoryException" ist in mscorlib.dll aufgetreten.
 Wie ließe sich der Code dahingehend optimieren, dass die Datei 
 doch noch manipuliert werden kann?
 Oder ist hier nur die hardwaremäßige Aufrüstung möglich?
 Ach ja, OS: Win7, RAM: 4GB
 Danke und Gruß
 CfB |  |  |  
| Th69 
          
  Beiträge: 4800
 Erhaltene Danke: 1059
 
 Win10
 C#, C++ (VS 2017/19/22)
 
 | 
Verfasst: Do 11.09.14 12:03 
 
Hallo und    wäre es nicht besser, du liest die Datei zeilenweise und schreibst dann auch immer nur jeweils die geänderte Zeile wieder weg (anstatt die ganze Datei zu laden, zu ändern und wieder abzuspeichern).
 Stichworte: StreamReader.ReadLine  und StreamWriter.WriteLine |  |  |  
| CodingForBeer  Hält's aus hier
 Beiträge: 7
 
 
 
 
 | 
Verfasst: Do 11.09.14 12:07 
 
HeyHo,
 danke für's     Aber ja, Du hast recht.
 An das zeilenweise Einlesen und Wegschreiben
 hatte ich gar nicht gedacht.
 Probier ich gleich mal aus.
 Rückmeldung gibt's dann auch.    Danke und Gruß
 CfB |  |  |  
| Palladin007 
          Beiträge: 1282
 Erhaltene Danke: 182
 
 Windows 11 x64 Pro
 C# (Visual Studio Preview)
 
 | 
Verfasst: Do 11.09.14 12:34 
 
Und schau dir mal das Stichwort using  an.
 Dann würde ich eigentlich auch eher die Open-Methode von File nutzen. Du bekommst du den FileStream, mit dem du dann einen StreamWriter oder StreamReader öffnen kannst.
 So muss die Datei nicht mehrfach geöffnet werden.
 Das direkt in der selben Datei zu ändern ist auch unglücklich, da du in der Datei nur umständlich lesen und gleichzeitig schreiben kannst und das dann vermutlich auch nur mit Problemen.
 Besser und sehr kompakt wäre es, wenn du mit den LINQtoObjects-Erweiterungsmethoden arbeitest und dann File.ReadLines und File.WriteAllLines verwendest. So musst du dich nicht selber um das Disposen kümmern und bei den beiden Methoden werden die Zeilen auch lazy nachgeladen.
 Was ich allerdings nicht weiß (das müsstest du ausprobieren), ist, ob der die Zeilen am Anfang im Speicher behält und das dann trotzdem eine Exception gibt.
 		                       C#-Quelltext 
 									| 1:2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
 
 | var filePath = "myCsvFile.csv";var tempFilePath = filePath + ".temp";
 
 File.Move(filePath, tempFilePath);
 
 var modifiedLines = File.ReadLines(tempFilePath).Select(line => line.Replace("\",", "\";"));
 File.WriteAllLines(filePath, modifiedLines);
 
 File.Delete(tempFilePath);
 |  Ist ungetestet, hab hier kein VS installiert, müsste aber funktionieren.
 Ob da dennoch der Speicher überläuft, kann ich dir leider nicht sagen, der Code ist meiner Meinung nach aber deutlich verständlicher. 
 Zuletzt bearbeitet von Palladin007 am Do 11.09.14 13:38, insgesamt 1-mal bearbeitet
 Für diesen Beitrag haben gedankt: CodingForBeer
 |  |  |  
| CodingForBeer  Hält's aus hier
 Beiträge: 7
 
 
 
 
 | 
Verfasst: Do 11.09.14 13:05 
 
Hallo Palladin007,
 absolut richtig.
 Das gleichzeitige Offenhalten und Schreiben in ein 
 und dieselbe Datei ist in der Tat problematisch.
 Zugriff verweigert...Datei wird schon benutzt...
 Datei ist nicht offen...und so weiter    Bei Deinem Ansatz muss ich wegen dem Zugriff zwar 
 auch mit einer temporären Datei arbeiten, aber
 das ist OK.
 Also, vielen Dank!
 Problem ist gelöst!
 CfB
 EDITH sagt:
 Die Zeile 5: File.Delete(filePath);
 muss ans Ende verschoben werden, da
 erst nach den Operationen die 
 temporäre Datei gelöscht werden soll.   |  |  |  
| Ralf Jansen 
          Beiträge: 4708
 Erhaltene Danke: 991
 
 
 VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
 
 | 
Verfasst: Do 11.09.14 13:28 
 
	  | Zitat: |  	  | Die Zeile 5: File.Delete(filePath); | 
 Ich hoffe du löscht am Ende tempFilePath und nicht filePath. Sonst wäre die ganze Aktion Zeitverschwendung    Da wo Paladin den File.Delete platziert hatte war der eh eher sinnfrei. Nach einem Move sollte da nix mehr sein. |  |  |  
| Palladin007 
          Beiträge: 1282
 Erhaltene Danke: 182
 
 Windows 11 x64 Pro
 C# (Visual Studio Preview)
 
 | 
Verfasst: Do 11.09.14 13:31 
 
In so einem Fall halte ich es für sinnvoll, eine temporäre Datei zu verwenden, auf der Festplatte ist schließlich mehr Platz als im RAM:
 Und du kannst gleichzeitig Schreiben und Lesen.
 Wobei, nicht ganz gleichzeitig, aber man kann eine Datei zum schreiben und lesen öffnen und auch beides machen.
 Das könnte sogar ganz gut funktionieren.
 
 Öffne mal einen Stream zum Lesen UND Schreiben (geht auch mit dem Konstruktor von FileStream) und erstelle damit dann einen StreamWriter und StreamReader.
 Ich könnte mir vorstellen, wenn du dort manuell nach Vorkommen vom Text suchst, den dort löschst und dann einfach den neuen String schreibst, hast du genau das, was du willst.
 Allerdings müsstest du das Suchen dann selber bauen, mit dem Nachteil, dass du nur Zeichen für Zeichen durchlaufen kannst.
 
 Wenn ich Zuhause bin, kann ich ja mal was basteln.
 Ist denke ich allgemein ganz praktisch, möglichst performant in großen Dateien ohne temporäre Datei ersetzen.
 
 
 Ralf:
 
 Ja, stimmt, Delet ist sinnfrei und ich hab am Ende das Move "zurück" vergessen.
 Werde ich gleich anpassen, danke für den Hinweis.
 |  |  |  
| Ralf Jansen 
          Beiträge: 4708
 Erhaltene Danke: 991
 
 
 VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
 
 | 
Verfasst: Do 11.09.14 13:32 
 
	  | Zitat: |  	  | Move "zurück" vergessen. | 
 Nö. Nur das löschen des Tmp File. |  |  |  
| Palladin007 
          Beiträge: 1282
 Erhaltene Danke: 182
 
 Windows 11 x64 Pro
 C# (Visual Studio Preview)
 
 | 
Verfasst: Do 11.09.14 13:39 
 
Was ist los mit mir? :/
 Ich merk schon, das Routieren in den Abteilungen bekommt mir nicht, ich roste ein    Wie auch immer, ich habs angepasst, müsste jetzt stimmen. |  |  |  
| CodingForBeer  Hält's aus hier
 Beiträge: 7
 
 
 
 
 | 
Verfasst: Do 11.09.14 14:04 
 
Höhö...
 rotierst Du in den Abteilungen oder rotieren die wegen Dir.     Und ja, ich lösche die tmp-Datei und nicht die eigentliche CSV-Datei.
 Mit dem Auslagern in eine zweite Datei ist das in Ordnung. 
 Hier spielt auch die Performance keine Rolle. Es werden Daten in eine DB 
 eingespielt und das geschieht maximal einmal in drei Monaten.
 Ob's 1 Minute oder 1 Stunde dauert...Who cares?    Allerdings ist mir eben etwas anderes passiert. Mit wurde doch tatsächlich 
 ein Timeout vor die Nase gesetzt.
 Die Daten schiebe ich per SqlBulkCopy in die DB: 
SqlBulkCopy bc = new SqlBulkCopy(con.ConnectionString, SqlBulkCopyOptions.TableLock); Das Hizufügen von 
bc.BulkCopyTimeout = 600; hat den Timeout dann schnell beseitigt.
 Mal sehen, ob das so noch mit einer 1,4 GB CSV funktioniert.
 Eventuell kommt dann doch wieder die MemoryException.     CfB |  |  |  
| Palladin007 
          Beiträge: 1282
 Erhaltene Danke: 182
 
 Windows 11 x64 Pro
 C# (Visual Studio Preview)
 
 | 
Verfasst: Do 11.09.14 14:18 
 
Die OutOfMemoryException kommt nur, wenn der Speicher voll läuft.
 Wenn du die Daten nicht im SPeicher behältst, sollte das kein Problem sein.
 Aber warum schreibst du erst in die Datei um dann in die Datenbank zu lesen? Lese doch direkt in die DB und passe die Zeilen in dem Zug direkt an.
 Was den BulkCopy angeht, kann ich leider nicht helfen.
 Das mit dem Timeout könnte ich mir aber so erklären, dass .NET auf eine Reaktion wartet, bei so vielen Daten dauert das aber.
 Wahrscheinlich bist du da besser beraten, wenn du die Daten in Abschnitte aufteilst und die dann einzeln bearbeitest.
 Aber bei dem Thema sollten besser die helfen, die auch genug Erfahrung haben, ich kenne nur die Grundlagen der POCOs von EF   |  |  |  
| CodingForBeer  Hält's aus hier
 Beiträge: 7
 
 
 
 
 | 
Verfasst: Do 11.09.14 15:07 
 
Hi,
 die Dateien schreibe ich deswegen um, weil sie später noch benötigt werden und
 dann nur in der manipulierten Form vorliegen sollen.
 Inhaltlich sehen die CSV-Dateien so aus (verkürzt):
"Spalte1","Spalte2","Spalte3","Spalte4",...,"Spalte11" "Eintrag1","Eintrag2","Eintrag3","Eintrag4",...,"Eintrag11"
 "Eintrag12","Eintrag13","Eintrag14","Eintrag15",...,"Eintrag22"
 ...
 Leider kann es auch passieren, dass innerhalb eines Wortes ein Komma auftaucht:
"Spalte1","Spalte2","Spalte3","Spalte4",...,"Spalte11" "Eintrag1","Eintrag2","Eintrag,3","Eintrag4",...,"Eintrag11"
 "Eintrag12","Eintrag13","Eintrag14","Eintrag15",...,"Eintrag22"
 ...
 Auch leere Wörter gibt es durchaus:
"Spalte1","Spalte2","Spalte3","Spalte4",...,"Spalte11" "Eintrag1","","Eintrag3","Eintrag4",...,"Eintrag11"
 "Eintrag12","Eintrag13","Eintrag14","Eintrag15",...,""
...
 Im ersten Schritt ersetze ich "" durch "NULL" 
 Somit gibt es keine leeren Wörter mehr.
 Im zweiten Schritt ersetze ich ", durch ";
 Somit ersetze ich nur die worttrennenden Kommas (oder heißt das Kommata?).
 Die innerhalb eines Wortes bleiben erhalten.
 Im dritten Schritt entferne ich alle "
 so dass die CSV-Datei wie folgt aussieht:
Spalte1;Spalte2;Spalte3;Spalte4;...;Spalte11 Eintrag1;NULL;Eintrag,3;Eintrag4;...;Eintrag11
 Eintrag12;Eintrag13;Eintrag14;Eintrag15;...;NULL
 ...
 Anhand der Semikolons (oder heißt das Semikola?) trenne ich das dann in die einzelnen 
 Wörter auf, die in die jeweiligen DB-Tabellen wandern.
 Schleifengesteuert lese ich alle CSV-Dateien eines Verzeichnisses ein und erzeuge anhand
 der Dateinamen die Tabellennamen (nach vorheriger Entfernung unerlaubter Zeichen).
 Die Anzahl der Spalten sowie ihre Bezeichnungen hole ich mir dann aus der jeweils ersten
 Zeile der CSV-Datei.
 Aber gut, ich will das hier jetzt nicht breit treten...ist ja schon Off-Topic verdächtig.    Gruß
 CfB |  |  |  |