2013-11-21 14 views
5

Come dice il titolo, mi piacerebbe poter leggere le righe da un file che si trova all'interno di un archivio zip, usando zip-conduit (i file zip Ho a che fare con lo molto grande, quindi devo essere in grado di farlo in memoria costante). Faccio schifo all'idea di base dei conduit, ma non li ho mai usati per la rabbia, e mi sento abbastanza bloccato da dove cominciare. Ho letto il tutorial sui conduit, ma sto riscontrando problemi nell'abbinamento con il mio problema.Leggi le righe da un file all'interno di un archivio zip usando il conduit zip di Haskell

La documentazione zip-condotto dice una possibile fonte da un archivio zip con qualcosa di simile a quanto segue:

import qualified Data.Conduit.Binary as CB 
import Codec.Archive.Zip 

withArchive archivePath $ do 
    name:_ <- entryNames 
    sourceEntry name $ CB.sinkFile name 

Presumo quello che devo fare è scrivere qualcosa al posto di CB.sinkFile. Data.Conduit.Text ha una funzione lines - potrebbe essere utilizzata in qualche modo per estrarre le righe dal file?

Apprezzerei molto un semplice esempio, ad esempio utilizzando putStrLn per scrivere le righe di un semplice file di testo archiviato all'interno di un file zip. Grazie in anticipo.

risposta

6

risposta di Michael, ma con zip-conduit:

import   Control.Monad.IO.Class (liftIO) 
import   Data.Conduit 
import qualified Data.Conduit.List as CL 
import qualified Data.Conduit.Text as CT 
import   Codec.Archive.Zip 

main :: IO() 
main = withArchive "input.zip" $ do 
    n:_ <- entryNames 
    sourceEntry n 
    $ CT.decode CT.utf8 
    =$ CT.lines 
    =$ CL.mapM_ (\t -> liftIO $ putStrLn $ "Got a line: " ++ show t) 
+0

Grazie mille, questo ha molto più senso. L'uso del conduit ha reso il mio codice molto più pulito. – Chris

1

Ecco un semplice esempio:

import   Control.Monad.IO.Class (liftIO) 
import   Data.Conduit 
import qualified Data.Conduit.Binary as CB 
import qualified Data.Conduit.List  as CL 
import qualified Data.Conduit.Text  as CT 

main :: IO() 
main = runResourceT 
    $ CB.sourceFile "input.txt" 
    $$ CT.decode CT.utf8 
    =$ CT.lines 
    =$ CL.mapM_ (\t -> liftIO $ putStrLn $ "Got a line: " ++ show t) 

È anche possibile view and experiment on FP Haskell Center.

+1

Grazie per prendere il tempo di rispondere, Michael. Il tuo esempio dimostra l'uso generale dei conduttori (che ho capito dal tutorial sui conduit), ma non illustra come si userebbe il conduit zip come descritto nella mia domanda, e temo di essere troppo stupido per saltare immediatamente dal tuo esempio a una soluzione. Un ulteriore aiuto sarebbe davvero apprezzato! – Chris

+0

L'esempio non funziona: ottenendo "Variabile non nell'ambito: main :: [GHC.Types.Char] -> t" – Christophe

1

Ecco un semplice esempio-

import Data.ByteString as B 
import Data.Conduit 
import qualified Data.Conduit.List as CL 
import qualified Data.Conduit.Binary as CB 
import Codec.Archive.Zip 
import System.Environment 

sink :: Monad m => Sink ByteString m [ByteString] 
sink = CL.consume 

main::IO() 
main = do 
    [archivePath] <- getArgs 
    res <- withArchive archivePath $ do 
     name:_ <- entryNames 
     source <- getSource name 
     runResourceT $ (source $$ sink) 

    print res 

È possibile elaborare i dati come viene attraverso nella funzione lavandino (che consumano come necessario usando CL, funzioni CB), o dal momento che i dati vengono restituiti pigramente , puoi modificare i dati in res.

+0

Grazie per questo, @jamshidh. Ma, in che modo esattamente dovrei elaborare le cose nella funzione sink? Se converto 'res' in una lista di stringhe e le elaboro, trovo che ci sono molti meno elementi in questa lista di quanto dovrebbero esserlo. Il numero di elementi sembra essere limitato dalla memoria richiesta. Non sono sicuro che 'res' sia pigro (la documentazione dice che' CB.consume' mette tutti i valori in memoria). Come si modificherebbe la funzione 'sink 'per processare banalmente ogni riga (ad esempio, aggiungere una stringa ad ogni riga)? – Chris