我想读取文件的最后一行,并确保它的字段数与第一行相同——我不关心中间的任何内容。我使用 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\",
Great.
From here http://hackage.haskell.org/package/containers-0.5.9.1/docs/Data-Sequence.html#v:takeWhileR, 我知道
takeWhileR p xs 等价于reverse (takeWhileL p (reverse
xs))。
那么让我们来做这个吧。也就是说,让我们通过反转我的惰性字节串来获取最后一行,像以前一样采用 while not "\n",然后取消反转它。懒惰让我认为编译器会让我轻松地做到这一点。
所以尝试一下这个:
LB.reverse (LB.takeWhile (`notElem` "\n") (LB.reverse outh))
我期望看到的是:
"\"val1\",\"val2\",
相反,这会导致我的会话崩溃。
Segmentation fault (core dumped)
问题:
- 我在懒惰、字节串、mmap 库或 Haskell 方面做错了什么?
- 我怎样才能正确且高效地获得这条线? (答案可能是使用外来指针而不是惰性字节串?)
对于其他读者,如果您想要获取最后一行,您可能会在此处的答案中找到一种非常快速且合适的方法:Haskell 中的 hSeek 和 SeekFromEnd https://stackoverflow.com/questions/41654849/hseek-and-seekfromend-in-haskell
在这个线程中,我正在专门寻找使用 mmap 的解决方案。
我更喜欢使用bytestring-mmap https://hackage.haskell.org/package/bytestring-mmap-0.2.2由同一作者制作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
特点。
另外,我不建议反转字节串——这至少需要一些分配,在这种情况下这是完全可以避免的。即使您使用惰性字节串,您仍然需要支付遍历字节串的所有块的成本(希望此时甚至不应该构造这些块)。也就是说,你的逆向代码should工作。我认为有什么问题mmap
(可能这个包用严格的字节串做同样的事情就可以了)。
之前的答案,来自OP编辑之前
我不确定其中的功能有什么问题System.IO https://hackage.haskell.org/package/base-4.9.1.0/docs/System-IO.html。以下内容立即在我的笔记本电脑上运行,文件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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)