2017-03-08 9 views
4

Ich frage mich, ob es eine einfache Möglichkeit gibt, Zeilen nacheinander aus einer Datei zu bekommen, ohne die gesamte Datei im Speicher zu laden. Ich würde gerne eine Falte über die Linien mit einem attoparsec Parser machen. Ich habe versucht mit Data.Text.Lazy.IO mit hGetLine und das bläst durch mein Gedächtnis. Ich lese später, dass schließlich die ganze Datei lädt.Lesen Sie große Zeilen in großer Datei ohne Puffer

Ich habe auch versucht Rohre Text mit folds und view lines mit:

s <- Pipes.sum $ 
    folds (\i _ -> (i+1)) 0 id (view Text.lines (Text.fromHandle handle)) 
print s 

nur zählt die Anzahl der Zeilen und es scheint etwas wackelig stuff „hGetChunk zu tun: ungültiges Argument (ungültige Bytefolge) "und es dauert 11 Minuten, wo wc -l 1 Minute dauert. Ich habe gehört, dass Pipes-Text könnte einige Probleme mit gigantischen Linien haben? (Jede Zeile ist über 1GB)

Ich bin wirklich offen für alle Vorschläge, kann nicht viel suchen außer für Anfänger readLine how-tos finden.

Danke!

+3

Sie verwenden Pipes.Text.IO für Eingabe anstelle von Pipes.Bytestring + Entschlüsselung, wie die Bibliothek rät. Die Fehlermeldung stammt aus der Textbibliothek, die für jeden Chunk eine Beurteilung über die Systemdecodierung vornimmt. Ich nehme an, es sagt, dass es den Brocken nicht verstehen kann, was auch immer er für die Kodierung hält. – Michael

Antwort

6

Der folgende Code verwendet Conduit und wird:

  • UTF8-decode Standardeingabe
  • Führen Sie das lineC combinator solange es mehr Daten zur Verfügung stehen
  • Für jede Zeile einfach yield den Wert 1 und verwerfen den Zeileninhalt, ohne jemals die gesamte Zeile in den Speicher auf einmal zu lesen
  • Summieren Sie die 1 s ergab und drucken Sie

Sie können den Code yield 1 durch etwas ersetzen, das die Verarbeitung der einzelnen Zeilen übernimmt.

#!/usr/bin/env stack 
-- stack --resolver lts-8.4 --install-ghc runghc --package conduit-combinators 
import Conduit 

main :: IO() 
main = (runConduit 
    $ stdinC 
    .| decodeUtf8C 
    .| peekForeverE (lineC (yield (1 :: Int))) 
    .| sumC) >>= print 
+1

Wow, super! 1m23s vs 1m5s für "wc-l", danke! –

+0

Gut zu hören, danke für die Berichterstattung. –

+0

Nicht sicher, ob dies der beste Platz ist, um zu fragen, aber wenn ich eine FalteC anstelle von SumC über meine analysierten Linien auf einem 3 int konstanten Größe Monoid, es scheint, um alle meine Erinnerung wieder ausblasen. Verlasse ich etwas? Ich habe auch versucht, foldlC. –

3

Dies ist wahrscheinlich am einfachsten als eine Falte über den Text-Stream decodiert

{-#LANGUAGE BangPatterns #-} 
import Pipes 
import qualified Pipes.Prelude as P 
import qualified Pipes.ByteString as PB 
import qualified Pipes.Text.Encoding as PT 
import qualified Control.Foldl as L 
import qualified Control.Foldl.Text as LT 
main = do 
    n <- L.purely P.fold (LT.count '\n') $ void $ PT.decodeUtf8 PB.stdin 
    print n 

Es dauert etwa 14% länger als wc -l für die Datei, die ich produziert, die nur lange Reihen von Komma und Ziffern war. IO sollte ordnungsgemäß mit Pipes.ByteString getan werden, wie die Dokumentation sagt, der Rest ist Annehmlichkeiten verschiedener Art.

Sie können view lines, einen attoparsec Parser über jede Zeile unterschieden Karte aber denken Sie daran, dass ein attoparsec Parser den ganzen Text ansammeln kann, was sie will und dies könnte nicht eine gute Idee, über ein 1-Gigabyte-Stück Text sein. Wenn es in jeder Zeile eine wiederholte Zahl gibt (z. B. wortgetrennte Zahlen), können Sie sie mit Pipes.Attoparsec.parsed streamen.

+0

Danke! Ich werde es auch versuchen, wenn ich eine Chance bekomme –

Verwandte Themen