2014-06-05 12 views
5

Ho scritto un codice seguente analisi utilizzando attoparsec:Conversione normale codice attoparsec parser al condotto/tubo basato

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 

Questo funziona bene per i file di piccole dimensioni, ho eseguito in questo modo:

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

Ma quando la dimensione del file è maggiore di 70 MB, consuma tonnellate di memoria. Come soluzione, ho pensato di utilizzare attoparsec-conduit. Dopo aver esaminato il loro API, non sono sicuro di come farli funzionare insieme. Il mio parser ha il tipo Parser Test ma è sinkParser effettivamente accetta il parser del tipo Parser a b. Sono interessato a come eseguire questo parser nella memoria costante? (Una soluzione basata su pipe è accettabile, ma non sono utilizzata per l'API Pipes.)

risposta

5

Il primo parametro di tipo su Parser è solo il tipo di dati dell'ingresso (o Text o ByteString). Puoi fornire la tua funzione testParser come argomento a sinkParser e funzionerà correttamente. Ecco un breve esempio:

{-# 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

Ecco la soluzione pipes (supponendo che si sta utilizzando un parser based Text):

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)