Gibt es eine Option "Gehe zu Zeile" in TextReader / StreamReader?

8

Ich habe eine riesige Textdatei mit 25k Zeilen. Innerhalb dieser Textdatei beginnt jede Zeile mit "1 \ t (linsumber)"

Beispiel:

%Vor%

Frage: Wie lese ich direkt zB Zeile 5?

    
Ivan Prodanov 31.05.2009, 12:30
quelle

5 Antworten

10

Sie können meine Klasse LineReader verwenden (entweder die in MiscUtil oder eine einfache Version hier ), um IEnumerable<string> zu implementieren und dann LINQ:

zu verwenden %Vor%

Dies setzt .NET 3.5 voraus. Andernfalls öffne ein TextReader (z. B. mit File.OpenText ) und rufe einfach ReadLine() viermal auf, um die Zeilen zu überspringen, die du nicht willst, und dann noch einmal, um die fünfte Zeile zu lesen.

Es gibt keine Möglichkeit, das zu "verkürzen", wenn Sie nicht genau wissen, wie viele Bytes in jeder Zeile sind.

    
Jon Skeet 31.05.2009, 12:37
quelle
3

Wenn Sie mit einem Datenformat fester Breite arbeiten (dh Sie wissen, dass alle Zeilen dieselbe Länge haben), können Sie die Länge mit Ihrer gewünschten Zeilennummer multiplizieren und Stream.Seek verwenden, um den Startpunkt von zu finden die n-te Zeile.

Wenn die Zeilen keine feste Länge haben, müssen Sie die richtige Anzahl von Zeilenumbrüchen finden, bis Sie am Anfang der gewünschten Zeile stehen. Das wäre am einfachsten mit StreamReader.ReadLine erledigt. (Sie können eine Erweiterungsmethode machen, um die Datei in IEnumerable & lt; string & gt; zu erstellen, wie Jon Skeet vorschlägt - das würde Ihnen eine schönere Syntax bringen, aber unter der Haube werden Sie ReadLine verwenden).

Wenn Leistung ein Problem ist, könnte es (ein bisschen) effizienter sein, nach & lt; CR & gt; & lt; LF & gt; Bytesequenzen in der Datei manuell mit der Stream.Read-Methode. Ich habe das nicht getestet; aber der StreamReader muss offensichtlich etwas Arbeit machen, um eine Zeichenkette aus der Byte-Folge zu konstruieren - wenn Sie sich nicht um die ersten Zeilen kümmern, kann diese Arbeit gespeichert werden, also sollten Sie theoretisch eine bessere Scan-Methode erstellen können . Dies wäre jedoch viel mehr Arbeit für Sie.

    
driis 31.05.2009 12:39
quelle
3

Sie können nicht direkt zu einer Zeile in einer Textdatei springen, es sei denn, jede Zeile hat eine feste Breite und Sie verwenden eine Codierung mit fester Breite (dh nicht UTF-8 - das ist jetzt eines der am häufigsten verwendeten). p>

Die einzige Möglichkeit ist, Zeilen zu lesen und diejenigen zu verwerfen, die Sie nicht wollen.

Alternativ können Sie einen Index am Anfang der Datei (oder in einer externen Datei) einfügen, der (z. B.) angibt, dass Zeile 1000 bei Byte-Offset [x] beginnt, Zeile 2000 bei Byte-Offset [y] beginnt. usw. Verwenden Sie dann .Position oder .Seek() für FileStream , um zum nächsten indizierten Punkt zu gelangen, und gehen Sie vorwärts.

Unter der Annahme des einfachsten Ansatzes (kein Index) sollte der Code in Jons Beispiel gut funktionieren. Wenn Sie LINQ nicht möchten, können Sie in .NET 2.0 + C # 2.0 etwas Ähnliches einwerfen:

%Vor%

Wenn Sie Werte der Linie testen müssen (anstatt nur Linienindex), dann ist das auch einfach genug; Ändern Sie einfach den Iteratorblock.

    
Marc Gravell 31.05.2009 12:58
quelle
1

Wenn Sie viele verschiedene Zeilen aus der Datei suchen (aber nicht alle), können Sie beim Erstellen eines Indexes von Vorteil sein. Verwenden Sie einen der Vorschläge, die bereits hier vorhanden sind, aber erstellen Sie währenddessen ein Array von Byte-Offsets für alle Zeilen, die Sie bereits gefunden haben, so dass Sie die Datei nicht jedes Mal vom Anfang an erneut scannen müssen / p>

ANHANG:
Es gibt noch einen Weg, wie Sie es schnell machen können, wenn Sie nur die gelegentliche "zufällige" Linie brauchen, aber auf Kosten einer komplizierteren Suche (Wenn Jons Antwort schnell genug ist, bleibe ich der Einfachheit halber definitiv dabei).

Sie könnten eine 'binäre Suche' durchführen, indem Sie einfach nach der Hälfte der Datei für die Sequenz '1' suchen, das erste Vorkommen, das Sie finden, gibt Ihnen eine Vorstellung davon, welche Zeilennummer Sie gefunden haben; dann basierend darauf, wo die gesuchte Zeile relativ zur gefundenen Nummer ist, die Sie rekursiv aufteilen.

Für zusätzliche Leistung können Sie auch davon ausgehen, dass die Linien ungefähr die gleiche Länge haben und der Algorithmus die ungefähre Position der von Ihnen gesuchten Linie relativ zur Gesamtanzahl der Zeilen in der Datei "erraten" und dann ausführen diese Suche von dort ab. Wenn Sie keine Annahmen über die Länge der Datei treffen möchten, können Sie sie sogar selbst erstellen, indem Sie sie zuerst in zwei Hälften teilen und die Zeilennummer zuerst als Annäherung an die Anzahl der Zeilen in der Datei verwenden ein ganzes.

Definitiv nicht trivial zu implementieren, aber wenn Sie viel Zugriff in Dateien mit einer großen Anzahl von Zeilen haben, kann es sich in Leistungsgewinnen auszahlen.

    
jerryjvl 31.05.2009 13:08
quelle
0

Wenn Sie mit einer Funktion, die ReadLine () im Hintergrund hat, in der Lage sein müssen, zu Zeile 24.000 zu springen, ist das ein bisschen langsam.

Wenn die Zeilennummer hoch ist, möchten Sie vielleicht eine fundierte Vermutung darüber geben, wo sich die Zeile in der Datei befindet, und von dort aus lesen. Damit Sie zu Zeile 24.567 gelangen, müssen Sie nicht zuerst 24.566 Zeilen lesen. Sie können zu einer Stelle in der Mitte springen, um herauszufinden, auf welcher Zeile Sie sich befinden, basierend auf der Zahl nach dem / t und dann von dort aus zählen.

Vor einiger Zeit habe ich mit einem Entwickler gearbeitet, der eine Datenbank erstellen musste, bevor RDBMS üblich waren. Seine Lösung für Ihr Problem war ähnlich dem, worüber ich gerade geschrieben habe, aber in seinem Fall hielt er eine Karte in einer separaten Datei. Die Karte kann jede hundertste Zeile an ihrem Speicherort im Dokument abbilden. Eine solche Karte kann sehr schnell geladen werden, was die Lesezeiten erhöhen kann. Zu der Zeit war sein System sehr schnell und effizient für schreibgeschützte Daten, aber nicht sehr gut für Lese- / Schreibdaten. (Jedes Mal, wenn Sie die Linien ändern, müssen Sie die gesamte Karte ändern, dies ist nicht sehr effizient)

    
Sruly 31.05.2009 15:59
quelle

Tags und Links