2014-06-05 4 views
5

ich folgenden Parsing-Code attoparsec mit geschrieben haben:normalen attoparsec Parsercode Konvertieren der Leitung/Rohr basiert

data Test = Test { 
    a :: Int, 
    b :: Int 
    } deriving (Show) 

testParser :: Parser Test 
testParser = do 
    a <- decimal 
    tab 
    b <- decimal 
    return $ Test a b 

tParser :: Parser [Test] 
tParser = many' $ testParser <* endOfLine 

Dies funktioniert gut für kleine Dateien, führe ich es wie folgt aus:

main :: IO() 
main = do 
    text <- TL.readFile "./testFile" 
    let (Right a) = parseOnly (manyTill anyChar endOfLine *> tParser) text 
    print a 

Wenn die Dateigröße jedoch größer als 70 MB ist, verbraucht sie viel Speicherplatz. Als Lösung dachte ich, ich würde attoparsec-conduit verwenden. Nachdem ich ihre API durchgegangen bin, bin ich nicht sicher, wie man sie zusammen arbeiten lässt. Mein Parser hat den Typ Parser Test, aber es ist sinkParser akzeptiert tatsächlich Parser vom Typ Parser a b. Ich bin daran interessiert, wie dieser Parser im konstanten Speicher ausgeführt wird? (A Rohre basierende Lösung ist auch akzeptabel, aber ich bin nicht auf die Rohre API verwendet.)

Antwort

5

Die ersten Typ-Parameter auf Parser ist nur der Datentyp des Eingangs (entweder Text oder ByteString). Sie können Ihre testParser Funktion als Argument für sinkParser bereitstellen und es wird gut funktionieren. Hier ist ein kurzes Beispiel:

{-# LANGUAGE OverloadedStrings #-} 
import   Conduit     (liftIO, mapM_C, runResourceT, 
              sourceFile, ($$), (=$)) 
import   Data.Attoparsec.Text (Parser, decimal, endOfLine, space) 
import   Data.Conduit.Attoparsec (conduitParser) 

data Test = Test { 
    a :: Int, 
    b :: Int 
    } deriving (Show) 

testParser :: Parser Test 
testParser = do 
    a <- decimal 
    space 
    b <- decimal 
    endOfLine 
    return $ Test a b 

main :: IO() 
main = runResourceT 
    $ sourceFile "foo.txt" 
    $$ conduitParser testParser 
    =$ mapM_C (liftIO . print) 
5

Hier ist die pipes Lösung (unter der Annahme, dass Sie einen Text -basierte Parser verwenden):

import Pipes 
import Pipes.Text.IO (fromHandle) 
import Pipes.Attoparsec (parsed) 
import qualified System.IO as IO 

main = IO.withFile "./testfile" IO.ReadMode $ \handle -> runEffect $ 
    for (parsed (testParser <* endOfLine) (fromHandle handle)) (lift . print) 
Verwandte Themen