无需缓冲即可读取大文件中的大行
Read large lines in huge file without buffering
我想知道是否有一种简单的方法可以从文件中一次获取一行,而无需最终将整个文件加载到内存中。我想用 attoparsec 解析器对线条进行折叠。我尝试将 Data.Text.Lazy.IO
与 hGetLine
一起使用,这让我记忆犹新。我后来读到最终加载了整个文件。
我还尝试将 pipes-text 与 folds
和 view lines
一起使用:
s <- Pipes.sum $
folds (\i _ -> (i+1)) 0 id (view Text.lines (Text.fromHandle handle))
print s
只是计算行数,它似乎在做一些奇怪的事情 "hGetChunk: invalid argument (invalid byte sequence)",它需要 11 分钟,而 wc -l
需要 1 分钟。我听说 pipes-text 可能有一些关于巨大线条的问题? (每行约1GB)
我非常乐于接受任何建议,除了新手 readLine
操作指南外找不到太多搜索。
谢谢!
以下代码使用 Conduit,并将:
- UTF8-解码标准输入
- 运行
lineC
组合器只要有更多可用数据
- 对于每一行,只需
yield
值 1
并丢弃该行内容,而不是一次将整行读入内存
- 汇总生成的
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
这可能是最简单的折叠解码文本流
{-#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
对于我制作的只有一长串逗号和数字的文件,它比 wc -l
长了大约 14%。如文档所述,IO 应该使用 Pipes.ByteString
正确完成,剩下的就是各种便利。
您可以在每一行上映射一个 attoparsec 解析器,以 view lines
区分,但请记住,attoparsec 解析器可以随意累积整个文本,这可能不是一个好主意 1千兆字节的文本块。如果每行有一个重复的数字(例如单词分隔的数字),您可以使用 Pipes.Attoparsec.parsed
来流式传输它们。
我想知道是否有一种简单的方法可以从文件中一次获取一行,而无需最终将整个文件加载到内存中。我想用 attoparsec 解析器对线条进行折叠。我尝试将 Data.Text.Lazy.IO
与 hGetLine
一起使用,这让我记忆犹新。我后来读到最终加载了整个文件。
我还尝试将 pipes-text 与 folds
和 view lines
一起使用:
s <- Pipes.sum $
folds (\i _ -> (i+1)) 0 id (view Text.lines (Text.fromHandle handle))
print s
只是计算行数,它似乎在做一些奇怪的事情 "hGetChunk: invalid argument (invalid byte sequence)",它需要 11 分钟,而 wc -l
需要 1 分钟。我听说 pipes-text 可能有一些关于巨大线条的问题? (每行约1GB)
我非常乐于接受任何建议,除了新手 readLine
操作指南外找不到太多搜索。
谢谢!
以下代码使用 Conduit,并将:
- UTF8-解码标准输入
- 运行
lineC
组合器只要有更多可用数据 - 对于每一行,只需
yield
值1
并丢弃该行内容,而不是一次将整行读入内存 - 汇总生成的
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
这可能是最简单的折叠解码文本流
{-#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
对于我制作的只有一长串逗号和数字的文件,它比 wc -l
长了大约 14%。如文档所述,IO 应该使用 Pipes.ByteString
正确完成,剩下的就是各种便利。
您可以在每一行上映射一个 attoparsec 解析器,以 view lines
区分,但请记住,attoparsec 解析器可以随意累积整个文本,这可能不是一个好主意 1千兆字节的文本块。如果每行有一个重复的数字(例如单词分隔的数字),您可以使用 Pipes.Attoparsec.parsed
来流式传输它们。