2012-12-21 18 views
11

Ich habe eine csv-Datei mit 60 GB, an der ich einige Änderungen vornehmen muss. Der Kunde möchte einige Änderungen an den Dateidaten vornehmen, aber ich möchte die Daten in dieser Datei nicht neu generieren, da es vier Tage dauerte.Wie kann man eine CSV-Datei Zeile für Zeile lesen und bestimmte Zeilen ersetzen/bearbeiten?

Wie kann ich die Datei zeilenweise lesen (nicht alle in den Speicher laden!), Und Änderungen an diesen Zeilen vornehmen, indem ich bestimmte Werte usw. ersetze?

+1

in einem solchen Fall, warum Sie nicht Hadoop Karte reduzieren Versuchen Sie es mit .... –

+0

Sie können werden Änderungen nur möglich, wenn modifizierte Leitungslänge haben, die nicht original Zeilenlänge ein –

+0

Warum nicht einfach schreiben überschreitet neue Datei? Also: 1. Lesen 2. Ändern 3. Zu kopieren. Ist das etwas, das du nicht tun willst, oder suchst du nur nach einem "eleganten" Weg, dies zu tun? – StampedeXV

Antwort

13

Der Prozess würde wie folgt sein:

  1. eröffnen StreamWriter in eine temporären Datei.
  2. Öffnen Sie eine StreamReader in der Zieldatei.
  3. Für jede Zeile:
    1. Split der Text in Spalten auf einem Trennzeichen basiert.
    2. die Spalten für die Werte überprüfen Sie ersetzen möchten, und sie ersetzen.
    3. Verbinden Sie die Spaltenwerte wieder zusammen Ihre Trennzeichen verwendet.
    4. Schreiben Sie die Zeile in die temporäre Datei.
  4. Wenn Sie fertig sind, löschen Sie die Zieldatei und verschieben Sie die temporäre Datei in den Zieldateipfad.

Hinweis in Bezug auf die Schritte 2 und 3.1: Wenn Sie in der Struktur der Datei sicher sind, und es ist einfach genug, Sie all dies aus der Box, wie tun kann (Ich werde eine Probe in einem Moment umfassen). Es gibt jedoch Faktoren in einer CSV-Datei, die Aufmerksamkeit benötigen (z. B. wenn ein Trennzeichen in einem Spaltenwert verwendet wird). Sie können sich selbst durchprobieren, oder versuchen Sie eine existing solution.


Grund Beispiel nur StreamReader und StreamWriter mit:

var sourcePath = @"C:\data.csv"; 
var delimiter = ","; 
var firstLineContainsHeaders = true; 
var tempPath = Path.GetTempFileName(); 
var lineNumber = 0; 

var splitExpression = new Regex(@"(" + delimiter + @")(?=(?:[^""]|""[^""]*"")*$)"); 

using (var writer = new StreamWriter(tempPath)) 
using (var reader = new StreamReader(sourcePath)) 
{ 
    string line = null; 
    string[] headers = null; 
    if (firstLineContainsHeaders) 
    { 
     line = reader.ReadLine(); 
     lineNumber++; 

     if (string.IsNullOrEmpty(line)) return; // file is empty; 

     headers = splitExpression.Split(line).Where(s => s != delimiter).ToArray(); 

     writer.WriteLine(line); // write the original header to the temp file. 
    } 

    while ((line = reader.ReadLine()) != null) 
    { 
     lineNumber++; 

     var columns = splitExpression.Split(line).Where(s => s != delimiter).ToArray(); 

     // if there are no headers, do a simple sanity check to make sure you always have the same number of columns in a line 
     if (headers == null) headers = new string[columns.Length]; 

     if (columns.Length != headers.Length) throw new InvalidOperationException(string.Format("Line {0} is missing one or more columns.", lineNumber)); 

     // TODO: search and replace in columns 
     // example: replace 'v' in the first column with '\/': if (columns[0].Contains("v")) columns[0] = columns[0].Replace("v", @"\/"); 

     writer.WriteLine(string.Join(delimiter, columns)); 
    } 

} 

File.Delete(sourcePath); 
File.Move(tempPath, sourcePath); 
+0

Das ist definitiv der einfache und geradlinigste Weg zu gehen. – richard

+0

Ich habe es aktualisiert, um literale Vorkommen des Trennzeichens zu behandeln. – HackedByChinese

+0

Eine Sache, ich dachte nicht an die Größe.Der finale 'File.Move' wird wahrscheinlich sehr langsam sein. Stattdessen können Sie die temporäre Datei im selben Ordner wie die Quelldatei erstellen, dann die Quelle löschen und die temporäre Datei umbenennen (anstatt "GetTempFileName" und "File.Move" zu verwenden). – HackedByChinese

1

Lesen Sie einfach die Datei, Zeile für Zeile, mit Stream lesen, und dann REGEX verwenden! Das erstaunlichste Werkzeug der Welt.

using (var sr = new StreamReader(new FileStream(@"C:\temp\file.csv", FileMode.Open))) 
     { 
      var line = sr.ReadLine(); 
      while (!sr.EndOfStream) 
      { 
       // do stuff 

       line = sr.ReadLine(); 
      } 

     } 
Verwandte Themen