使用Haskell的zip-conduit从zip存档文件中读取文件内的行

6
正如标题所述,我想使用zip-conduit从一个压缩文件中读取行,我的压缩文件非常大,因此需要在恒定的内存中完成。我了解conduits的基本概念,但从未真正使用过,对于如何开始感到非常困惑。我已经阅读了conduits教程,但是很难将其与我的问题匹配起来。
zip-conduit文档表示可以通过以下方式从zip归档文件中获取源:
import qualified Data.Conduit.Binary as CB
import Codec.Archive.Zip

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

我猜我需要做的是在CB.sinkFile的位置上写一些内容。 Data.Conduit.Text有一个lines函数 - 是否可以以某种方式使用它来获取文件中的行?

我会非常感激一个简单的例子,比如使用putStrLn将存档在zip文件内的简单文本文件的行写出来。先行致谢。

3个回答

6

使用zip-conduit的Michael的答案:

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)

非常感谢,这让我更加明白了。使用conduit使我的代码更加简洁。 - Chris

1

Here's a simple example:

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)

您还可以查看并在FP Haskell Center上进行实验

1
感谢抽出时间回复,Michael。你的例子展示了conduits的一般用法(我从conduits教程中理解到),但并没有说明如何使用zip-conduit来解决我提出的问题,我恐怕太笨了,无法立即从你的例子中得出解决方案。真的非常需要进一步的帮助! - Chris
示例无法运行:出现“Variable not in scope: main :: [GHC.Types.Char] -> t”错误。 - Christophe

1
这是一个简单的例子-
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

你可以在sink函数中处理数据(使用CL、CB函数按需消耗),或者由于数据是惰性返回的,你可以在res中修改数据。

谢谢你,@jamshidh。但是,在sink函数中我应该如何处理这些东西呢?如果我将res转换为字符串列表并对其进行处理,我会发现此列表中的元素要少得多。项目数量似乎受它们所需的内存限制。我不确定res是否是惰性的(文档说CB.consume将所有值放入内存中)。如何修改您的sink函数以轻松处理每行(例如,将给定字符串附加到每行)? - Chris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接