Was ist der schnellste Weg, um Text mit benutzerdefinierten Trennzeichen und einige sehr, sehr große Feldwerte in C # zu analysieren?

StackOverflow https://stackoverflow.com/questions/339496

Frage

Ich habe versucht, mit einigen getrennten Textdateien zu beschäftigen, die Nicht-Standard-Trennzeichen (nicht Komma / quote oder Tabulator getrennt). Die Trennzeichen sind zufällige ASCII-Zeichen, die oft zwischen den Trennzeichen nicht angezeigt. Nach der Suche um, habe ich scheinen gefunden zu haben, nur keine Lösungen in .NET wird meine Bedürfnisse und die benutzerdefinierten Bibliotheken, die Menschen haben dafür einige Mängel zu haben scheinen geschrieben passen, wenn es mit einigen Feldwerte zu gigantischen Eingang (4 GB-Datei kommt sehr leicht mehrere Millionen Zeichen).

Während dies scheint ein bisschen extrem zu sein, ist es eigentlich ein Standard in der elektronischen Dokumenten Entdeckung (EDD) Industrie ist für einige Überprüfung Software Feldwert zu haben, die den gesamten Inhalt eines Dokuments enthält. Als Referenz habe ich dies das csv-Modul ohne Probleme mit in Python geschehen ist.

Hier ist ein Beispiel Eingabe:

Field delimiter = 
quote character = þ

þFieldName1þþFieldName2þþFieldName3þþFieldName4þ
þValue1þþValue2þþValue3þþSomeVery,Very,Very,Large value(5MB or so)þ
...etc...

Edit: Also ging ich weiter und erstellt eine Datei mit Trennzeichen-Parser von Grund auf neu. Ich bin ein bisschen müde mit dieser Lösung, da es zu Fehlern anfällig sein kann. Es fühlt sich auch nicht „elegant“ oder richtig zu haben, meine eigenen Parser für eine Aufgabe wie diese zu schreiben. Ich habe auch das Gefühl, dass ich wahrscheinlich nicht einen Parser von Grund auf für diese ohnehin zu schreiben hatte.

War es hilfreich?

Lösung

Mit dem Helfer API Datei. Es ist .NET und Open Source. Es ist eine extrem hohe Leistung mit kompilierten IL-Code Felder setzen auf stark typisierte Objekte und unterstützt Streaming.

Es unterstützt alle Arten von Dateitypen und benutzerdefinierten Trennzeichen; Ich habe es verwendet, um Dateien größer als 4 GB zu lesen.

Wenn Sie aus irgendeinem Grund, dass es nicht für Sie tut, versuchen Sie einfach Linie mit einem string.split für Zeile zu lesen:

public IEnumerable<string[]> CreateEnumerable(StreamReader input)
{
    string line;
    while ((line = input.ReadLine()) != null)
    {
        yield return line.Split('þ');
    }
}

Das gibt Ihnen einfaches String-Arrays die Linien in einer streamy Art und Weise darstellen, die Sie können sogar Linq in;) aber Denken Sie daran, dass der IEnumerable faul geladen ist, so tut die Stream nicht in der Nähe oder ändern, bis Sie iteriert haben ( oder verursachte einen Volllastbetrieb wie ToList / ToArray oder so -. angesichts Ihrer Dateigröße aber ich nehme an, Sie werden das nicht tun)

Hier ist ein gutes Beispiel Gebrauch davon:

using (StreamReader sr = new StreamReader("c:\\test.file"))
{
    var qry = from l in CreateEnumerable(sr).Skip(1)
              where l[3].Contains("something")
              select new { Field1 = l[0], Field2 = l[1] };
    foreach (var item in qry)
    {
        Console.WriteLine(item.Field1 + " , " + item.Field2);
    }
}
Console.ReadLine();

Dies wird die Kopfzeile überspringt, dann das beide erste Feld aus der Datei drucken, wo das vierte Feld die Zeichenfolge „etwas“ enthält. Es wird dies tun, ohne die gesamte Datei in den Speicher geladen werden.

Andere Tipps

Fenster und Hochleistungs-E / A-Mittel verwenden IO Completion Ports. Sie können todo einige zusätzliche Rohrleitungen müssen es in Ihrem Fall erhalten zu arbeiten.

Dies ist mit dem Verständnis, dass Sie C # /. NET verwenden möchten, und nach Joe Duffy

  

18) Verwenden Sie keine Windows-Asynchronous Procedure Calls (APCs) in verwaltetem   Code.

Ich hatte, dass man auf die harte Tour zu lernen;), aber APC Verwendung auszuschließen, IOCP ist die einzige vernünftige Option. Es unterstützt auch viele andere Arten von I / O, häufig in Socket-Server verwendet wird.

Was den eigentlichen Text Parsen Besuche Eric White Blog für einige rationalisierte Stream.

Ich würde geneigt sein, eine Kombination von Speicher zu verwenden Mapped Files ( Msdn Punkt zu einer .NET-Wrapper hier ) und einem einfachen inkrementalen Parsing, was zu einer IEnumerable Liste Ihrer Aufnahme / Textzeile (oder was auch immer)

Nachgeben zurück

Sie erwähnen, dass einige Felder sind sehr, sehr groß, wenn Sie versuchen, sie in ihrer Gesamtheit zu Speicher lesen Sie selbst in Schwierigkeiten zu geraten werden können. Ich würde durch die Datei in 8K (oder kleine Stücke) lesen, analysieren den aktuellen Puffer, verfolgen Zustand.

Was wollen Sie mit diesen Daten zu tun, die Sie Parsen? Suchen Sie etwas? Verwandeln Sie es?

Ich sehe nicht ein Problem mit einem benutzerdefinierten Parser zu schreiben. Die Anforderungen scheinen ausreichend anders als alles bereits vom BCL bereitgestellt, so gehen Sie nach rechts weiter.

„Elegance“ ist natürlich eine subjektive Sache. Meiner Meinung nach, wenn API Ihre Parser sieht aus und funktioniert wie ein Standard-BCL „Leser“ -Typ API, dann ist das ganz „elegant“.

Wie bei den großen Datengrößen, um Ihren Parser Arbeit um ein Byte zu einem Zeitpunkt, zu lesen und eine einfache Zustandsmaschine verwenden, um herauszufinden, was zu tun ist. Lassen Sie das Streaming und Pufferung der zugrunde liegenden FileStream Klasse. Sie sollten sich mit Leistung und Speicherverbrauch in Ordnung sein.

Beispiel dafür, wie man einen solchen Parser-Klasse verwenden können:

using(var reader = new EddReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 8192)) {
    // Read a small field
    string smallField = reader.ReadFieldAsText();
    // Read a large field
    Stream largeField = reader.ReadFieldAsStream();
}

Während dies nicht helfen, das großen Eingang Problem zu beheben, eine mögliche Lösung für das Parsen Problem könnte einen benutzerdefinierten Parser enthält, die Benutzer das Strategie-Muster ein Trennzeichen zu liefern.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top