我想读取文件的最后一行,并确保它的字段数与第一行相同——我不关心中间的任何内容。我使用 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)
问题:
- 我在懒惰、字节串、mmap 库或 Haskell 方面做错了什么?
- 如何才能正确且高效地获取此行? (答案可能是使用外部指针而不是惰性字节串?)
对于其他读者,如果您想要获取最后一行,您可能会在此处的答案中找到一种非常快速且合适的方法: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/