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

Posted

技术标签:

【中文标题】使用 Haskell 的 zip-conduit 从 zip 存档中的文件中读取行【英文标题】:Read lines from a file inside a zip archive using Haskell's zip-conduit 【发布时间】:2013-11-21 17:01:28 【问题描述】:

正如标题所说,我希望能够使用 zip-conduit 从 zip 存档中的文件中读取行(我正在处理的 zip 文件非常大,所以我需要能够在不断的记忆中做到这一点)。我理解了管道的基本概念,但从来没有在愤怒中使用过它们,并且对于从哪里开始感到很困惑。我已经阅读了管道教程,但我无法将其与我的问题相匹配。

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.sinkFileData.Conduit.Text 有一个 lines 函数——它可以以某种方式用于从文件中取出行吗?

我非常感谢一个简单的例子,比如使用putStrLn 写出一个简单的文本文件的行,该文件被归档在一个 zip 文件中。提前致谢。

【问题讨论】:

【参考方案1】:

迈克尔的回答,但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)

【讨论】:

非常感谢,这更有意义。使用导管使我的代码更加简洁。【参考方案2】:

这是一个简单的例子:

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)

你也可以view and experiment on FP Haskell Center。

【讨论】:

感谢您抽出宝贵时间回复,迈克尔。您的示例演示了导管的一般用途(我从导管教程中了解到),但没有说明如何使用我的问题中概述的 zip-conduit,恐怕我太笨了,无法立即跳转从您的示例到解决方案。进一步的帮助真的很感激! 示例不起作用:获取“变量不在范围内:main :: [GHC.Types.Char] -> t”【参考方案3】:

这是一个简单的例子-

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

您可以在接收器函数中处理数据(使用 CL、CB 函数根据需要使用),或者由于数据是延迟返回的,您可以在 res 中修改数据。

【讨论】:

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

以上是关于使用 Haskell 的 zip-conduit 从 zip 存档中的文件中读取行的主要内容,如果未能解决你的问题,请参考以下文章

Haskell 类型安全的空间使用

Haskell:使用严格性的指南

Haskell:为啥模式匹配中不允许使用 ++?

Haskell 多线程有多难?

Haskell,分析导入库的内存使用情况

是否有使用严格评估的 Haskell 编译器或预处理器?