在没有缓冲的情况下读取大文件中的大行

时间:2017-03-08 15:45:27

标签: haskell haskell-pipes

我想知道是否有一种简单的方法可以一次从文件中获取一行,而不会最终将整个文件加载到内存中。我想用 attoparsec 解析器对这些行进行折叠。我尝试将Data.Text.Lazy.IOhGetLine一起使用,这让我记忆深刻。我后来读到最终会加载整个文件。

我还尝试将 pipes-text foldsview lines一起使用:

s <- Pipes.sum $ 
    folds (\i _ -> (i+1)) 0 id (view Text.lines (Text.fromHandle handle))
print s

只计算行数,它似乎做了一些不稳定的东西&#34; hGetChunk:无效的参数(无效的字节序列)&#34; wc -l需要1分钟,需要11分钟。我听说 pipes-text 可能会遇到一些巨大的问题? (每行大约1GB)

我真的愿意接受任何建议,除了新手readLine之外,找不到多少搜索。

谢谢!

2 个答案:

答案 0 :(得分:7)

以下代码使用Conduit,并将:

  • UTF8-decode标准输入
  • 只要有更多可用数据,就运行lineC组合器
  • 对于每一行,只需yield1并丢弃行内容,而无需立即将整行读入内存
  • 总结1 s屈服并打印

您可以将yield 1代码替换为将在各行上处理的内容。

#!/usr/bin/env stack
-- stack --resolver lts-8.4 --install-ghc runghc --package conduit-combinators
import Conduit

main :: IO ()
main = (runConduit
     $ stdinC
    .| decodeUtf8C
    .| peekForeverE (lineC (yield (1 :: Int)))
    .| sumC) >>= print

答案 1 :(得分:3)

这可能是解码文本流的最简单

{-#LANGUAGE BangPatterns #-}
import Pipes 
import qualified Pipes.Prelude as P
import qualified Pipes.ByteString as PB
import qualified Pipes.Text.Encoding as PT
import qualified Control.Foldl as L
import qualified Control.Foldl.Text as LT
main = do
  n <- L.purely P.fold (LT.count '\n') $ void $ PT.decodeUtf8 PB.stdin
  print n

对于我生成的文件,它只需要大约14%的wc -l,这只是逗号和数字的长行。正如文档所述,IO应该使用Pipes.ByteString正确完成,其余的是各种各样的便利。

你可以在每行上映射一个attoparsec解析器,由view lines区分,但请记住,attoparsec解析器可以随意累积整个文本,这对于1 GB的块可能不是一个好主意的文字。如果每行上都有重复的数字(例如,单词分隔数字),您可以使用Pipes.Attoparsec.parsed来传输它们。