Haskell 使用惰性 mmap 读取最后一行

标签 haskell lazy-evaluation mmap

我想读取文件的最后一行,并确保它的字段数与第一行相同——我不关心中间的任何内容。我使用 mmap 是因为它可以快速随机访问大文件,但我遇到了不理解 Haskell 或懒惰的问题。

λ> import qualified Data.ByteString.Lazy.Char8 as LB
λ> import System.IO.MMap
λ> outh <- mmapFileByteStringLazy fname Nothing 
λ> LB.length outh
87094896
λ> LB.takeWhile (`notElem` "\n") outh
"\"Field1\",\"Field2\",

太棒了。

来自here ,我知道

takeWhileR p xs is equivalent to reverse (takeWhileL p (reverse xs)).

所以让我们来做这个吧。也就是说,让我们通过反转我的惰性字节串来获取最后一行,像以前一样采用 while not "\n",然后取消反转它。懒惰让我认为编译器会让我轻松地做到这一点。

所以尝试一下:

LB.reverse (LB.takeWhile (`notElem` "\n") (LB.reverse outh))

我期望看到的是:

"\"val1\",\"val2\",

相反,这会导致我的 session 崩溃。

Segmentation fault (core dumped)

问题:

  1. 我在懒惰、字节串、mmap 库或 Haskell 方面做错了什么?
  2. 如何才能正确且高效地获取此行? (答案可能是使用外部指针而不是惰性字节串?)

对于其他读者,如果您想要获取最后一行,您可能会在此处的答案中找到一种非常快速且合适的方法:hSeek and SeekFromEnd in Haskell

在这个帖子中,我正在专门寻找使用 mmap 的解决方案。

最佳答案

我更喜欢使用bytestring-mmap由与 bytestring 相同的作者制作。无论哪种情况,您所需要的只是

import System.IO.Posix.MMap (unsafeMMapFile)
import qualified Data.ByteString.Char8 as BS

main = do
   -- can be swapped out for `mmapFileByteString` from `mmap`
  bs <- unsafeMMapFile "file.txt"

  let (firstLine, _) = BS.break (== '\n') bs
      (_, lastLine) = BS.breakEnd (== '\n') bs

  putStrLn $ "First line: " ++ BS.unpack firstLine
  putStrLn $ "Last line: " ++ BS.unpack lastLine

这也可以立即运行,无需额外分配。和以前一样,需要注意的是许多文件以换行符结尾,因此可能希望使用 BS.breakEnd (== '\n') (init bs) 来忽略最后一个 \n 字符。

此外,我不建议反转字节串 - 这至少需要一些分配,在这种情况下这是完全可以避免的。即使您使用惰性字节串,您仍然需要支付遍历字节串的所有 block 的成本(希望此时甚至不应该构造这些 block )。也就是说,您的逆向代码应该可以工作。我认为 mmap 出了点问题(可能这个包用严格的字节串做同样的事情就可以了)。

之前的答案,来自OP编辑之前

我不确定 System.IO 中的函数有什么问题。以下代码在我的笔记本电脑上立即运行,文件 file.txt 几乎有 4GB。它并不优雅,但确实高效。

import System.IO

hGetLastLine :: Handle -> IO String
hGetLastLine hdl = go "" (negate 1)
  where
  go s i = do
    hSeek hdl SeekFromEnd i
    c <- hGetChar hdl
    if c == '\n'
      then pure s
      else go (c:s) (i-1)


main = do
  handle <- openFile "file.txt" ReadMode

  firstLine <- hGetLine handle
  putStrLn $ "First line: " ++ firstLine

  lastLine <- hGetLastLine handle
  putStrLn $ "Last line: " ++ lastLine

关于Haskell 使用惰性 mmap 读取最后一行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41656678/

相关文章:

haskell - 如何在haskell中生成随机命题公式(CNF)?

haskell - typeOf 返回类型

php - mysql 查询 php

haskell - 读取文件后 Haskell 文件会自动关闭吗?

c - 在 linux 上写入映射内存时使用 mmap 返回共享内存

docker - 为什么我的Haskell程序不能在带有Alpine或Scratch的Docker中运行?

loops - enumerator 与 iteratee 包的比较

Java 通过谓词将流拆分为流的流

php - 带有 mmap 缓存错误的 Apache 错误日志

linux - 为什么 msync() 不改变文件的 st_mtime