2015-07-06 9 views
5

Wie kann ich eine sehr lange Zeichenfolge aus einer Textdatei lesen und sie dann verarbeiten (in Wörter aufgeteilt)?Wie teilt man eine riesige Datei in Wörter?

Ich versuchte die StreamReader.ReadLine() Methode, aber ich bekomme eine OutOfMemory Ausnahme. Anscheinend sind meine Linien extrem lang. Dies ist mein Code für das Lesen von Datei:

using (var streamReader = File.OpenText(_filePath)) 
    { 

     int lineNumber = 1; 
     string currentString = String.Empty; 
     while ((currentString = streamReader.ReadLine()) != null) 
     { 

      ProcessString(currentString, lineNumber); 
      Console.WriteLine("Line {0}", lineNumber); 
      lineNumber++; 
     } 
    } 

Und der Code die Zeile in Worte teilt:

var wordPattern = @"\w+"; 
var matchCollection = Regex.Matches(text, wordPattern); 
var words = (from Match word in matchCollection 
      select word.Value.ToLowerInvariant()).ToList(); 
+0

Welchen Algorithmus/welche Methode haben Sie für den Splitvorgang verwendet? – byako

+0

@byako, hier ist Methode, die ich für diesen Einsatz: 'public static IEnumerable GetLowercasedWords (string text) { if (String.IsNullOrEmpty (Text)) { return null; } var wordPattern = @ "\ w +"; var matchCollection = Regex.Matches (text, wordPattern); var words = (aus Übereinstimmungswort in matchCollection word.Value.ToLowerInvariant() auswählen). ToList(); Rückworte; } Sorry für die Formatierung. –

+0

Fügen Sie besser hinzu, wie Sie den StreamReader tatsächlich implementiert haben, damit die Leute dort Fehler finden. Für Fragen gibt es keine 140 Zeichen. – Jens

Antwort

5

Sie von char lesen konnte, Wörter aufbauen, wie Sie gehen, yield mit ihm verschoben, so dass Sie die gesamte Datei auf einmal nicht lesen müssen:

private static IEnumerable<string> ReadWords(string filename) 
{ 
    using (var reader = new StreamReader(filename)) 
    { 
     var builder = new StringBuilder(); 

     while (!reader.EndOfStream) 
     { 
      char c = (char)reader.Read(); 

      // Mimics regex /w/ - almost. 
      if (char.IsLetterOrDigit(c) || c == '_') 
      { 
       builder.Append(c); 
      } 
      else 
      { 
       if (builder.Length > 0) 
       { 
        yield return builder.ToString(); 
        builder.Clear(); 
       } 
      } 
     } 

     yield return builder.ToString(); 
    } 
} 

Der Code liest die Datei durch Zeichen, und wenn es ein non-word Zeichen trifft, wird es yield return das bis dahin aufgebaute Wort (nur für das erste Nicht-Buchstabenzeichen). Der Code verwendet eine StringBuilder, um die Wortzeichenfolge zu erstellen.

Char.IsLetterOrDigit() verhält sich wie the regex word character w für Zeichen, aber Unterstriche (unter anderem) fallen auch in die letztere Kategorie. Wenn Ihre Eingabe mehr Zeichen enthält, die Sie ebenfalls hinzufügen möchten, müssen Sie die if() ändern.

+2

Wäre nicht 'StringBuilder' eine bessere Option für diesen Ansatz? –

+2

Vorsicht! Dies ist nicht äquivalent zu "\ w +", da es nichts außer Leerzeichen behandelt (z. B. Bindestriche, Interpunktion). – Bas

+0

'\ w' enthält auch Ziffern und Unterstriche. – Bas

0

Cut es in Bitgröße Abschnitte. Also, anstatt zu versuchen, 4GB zu lesen, was meiner Meinung nach ungefähr so ​​groß ist wie eine Seite, versuche 8 500 MB große Stücke zu lesen und das sollte helfen.

+0

Ich sollte sagen, dass Sie es nicht in genaue Stücke teilen werden. Aber relativ nah genug Stücke. Der Grund, warum ich das sage ist, dass, wenn Sie einen 500 mb Cutoff haben, Sie die Datei am Ende oder Anfang eines Wortes aufteilen möchten, nicht in der Mitte. Also zerschneide nicht einfach die Datei. Mach es klüger. – trinityalps

+0

du hast Recht. Und das ist der Hauptgrund, warum ich auf Schwierigkeiten stieß –

0

Müllsammlung kann eine Lösung sein. Ich bin nicht wirklich sicher, dass es die Problemquelle ist. Aber wenn das der Fall ist, ist ein einfaches GC.Collect oft nicht ausreichend und sollte aus Performancegründen nur aufgerufen werden, wenn es wirklich benötigt wird. Versuchen Sie die folgende Prozedur, die den Papierkorb aufruft, wenn der verfügbare Speicher zu niedrig ist (unter dem als Prozedurparameter angegebenen Schwellenwert).

int charReadSinceLastMemCheck = 0 ; 
using (var streamReader = File.OpenText(_filePath)) 
{ 

    int lineNumber = 1; 
    string currentString = String.Empty; 
    while ((currentString = streamReader.ReadLine()) != null) 
    { 

     ProcessString(currentString, lineNumber); 
     Console.WriteLine("Line {0}", lineNumber); 
     lineNumber++; 
     totalRead+=currentString.Length ; 
     if (charReadSinceLastMemCheck>1000000) 
     { // Check memory left every Mb read, and collect garbage if required 
      CollectGarbage(100) ; 
      charReadSinceLastMemCheck=0 ; 
     } 
    } 
} 


internal static void CollectGarbage(int SizeToAllocateInMo) 
{ 
     long [,] TheArray ; 
     try { TheArray =new long[SizeToAllocateInMo,125000]; }low function 
     catch { TheArray=null ; GC.Collect() ; GC.WaitForPendingFinalizers() ; GC.Collect() ; } 
     TheArray=null ; 
} 
+0

@CodeCaster: Sie haben in Ihren entfernten Kommentar geschrieben: "Sie sollten auch nicht den Voodoo-Code, den Sie in einem französischen Forum gefunden haben, cross-posten". Wenn es gegen StackOverflow-Prinzipien ist, Code zu duplizieren, den du bereits in einem anderen Forum gepostet hast, werde ich das Forum schnell verlassen. Wenn Sie sich den Autor der französischen Forum-Antwort ansehen, werden Sie sehen, dass sein Name auch "Graffito" ist. Aber du denkst sicher, es ist ein anderer Typ. – Graffito

+1

Graffito, deine CollectGarbage-Methode ist der Teufel. Ich erkenne es aus deiner früheren Antwort. – usr

Verwandte Themen