Haskell 中的 hSeek 和 SeekFromEnd
hSeek and SeekFromEnd in Haskell
我希望在 Haskell 中快速检索文件的最后一行---从结尾开始,而不是从开头---并且在正确使用 hSeek 时遇到一些困难。
似乎 SeekFromEnd N
的行为不同于查找文件长度 sz
和使用 AbsoluteSeek
去 (sz - N)
字节。
outh <- openFile "test.csv" ReadMode
λ> hIsSeekable outh
True
λ> hFileSize outh
81619956
λ> hSeek outh AbsoluteSeek 1000
λ> hTell outh
1000
λ> hSeek outh SeekFromEnd 1000
λ> hTell outh
81620956
λ> hSeek outh AbsoluteSeek 0
λ> hGetLine outh
"here's my data"
λ> hSeek outh SeekFromEnd 10000
-*** Exception: test.csv: hGetLine: end of file
嗯,这很奇怪。
所以,我创建了一个函数,用 absolute 代替:
λ> hSeek outh SeekFromEnd 100000
λ> hTell outh
81719956
fromEnd outh = do
sz <- hFileSize outh
hSeek outh AbsoluteSeek (sz - 100000)
λ> fromEnd outh
λ> hTell outh
81519956
所以在输出方面,他们有不同的答案,这很奇怪。此外,我现在还可以使用 hGetLine,SeekFromEnd 失败于:
λ> hGetLine outh
"partial output"
λ> hGetLine outh
"full output, lots of fields, partial output"
我不清楚这里发生了什么。为什么我的 fromEnd 在允许 hGetLine 方面的行为与 SeekFromEnd 不同?
问题的第二部分:从文件末尾开始并向后查找第一个换行符(EOF 换行符之后的第一个 \n)的正确策略是什么?
在这个问题中,我专门使用 SeekFromEnd 寻找答案。
SeekFromEnd
的偏移量预计为负数。
关于获取文件的最后一行,我们遇到了一个烦恼,我们必须从末尾扫描每个字符,一个一个地扫描,每次重置位置。就是说,我们 可以 做到这一点 - 我们只是继续往回移动,直到遇到第一个 \n
字符。
import System.IO
-- | Given a file handle, find the last line. There are no guarantees as to the
-- position of the handle after this call, and it is expected that the given
-- handle is seekable.
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)
你可能想在这里加一个减号,因为大多数文件通常以 \n
结尾(而那个空行可能不是你想要的)
我希望在 Haskell 中快速检索文件的最后一行---从结尾开始,而不是从开头---并且在正确使用 hSeek 时遇到一些困难。
似乎 SeekFromEnd N
的行为不同于查找文件长度 sz
和使用 AbsoluteSeek
去 (sz - N)
字节。
outh <- openFile "test.csv" ReadMode
λ> hIsSeekable outh
True
λ> hFileSize outh
81619956
λ> hSeek outh AbsoluteSeek 1000
λ> hTell outh
1000
λ> hSeek outh SeekFromEnd 1000
λ> hTell outh
81620956
λ> hSeek outh AbsoluteSeek 0
λ> hGetLine outh
"here's my data"
λ> hSeek outh SeekFromEnd 10000
-*** Exception: test.csv: hGetLine: end of file
嗯,这很奇怪。
所以,我创建了一个函数,用 absolute 代替:
λ> hSeek outh SeekFromEnd 100000
λ> hTell outh
81719956
fromEnd outh = do
sz <- hFileSize outh
hSeek outh AbsoluteSeek (sz - 100000)
λ> fromEnd outh
λ> hTell outh
81519956
所以在输出方面,他们有不同的答案,这很奇怪。此外,我现在还可以使用 hGetLine,SeekFromEnd 失败于:
λ> hGetLine outh
"partial output"
λ> hGetLine outh
"full output, lots of fields, partial output"
我不清楚这里发生了什么。为什么我的 fromEnd 在允许 hGetLine 方面的行为与 SeekFromEnd 不同?
问题的第二部分:从文件末尾开始并向后查找第一个换行符(EOF 换行符之后的第一个 \n)的正确策略是什么?
在这个问题中,我专门使用 SeekFromEnd 寻找答案。
SeekFromEnd
的偏移量预计为负数。
关于获取文件的最后一行,我们遇到了一个烦恼,我们必须从末尾扫描每个字符,一个一个地扫描,每次重置位置。就是说,我们 可以 做到这一点 - 我们只是继续往回移动,直到遇到第一个 \n
字符。
import System.IO
-- | Given a file handle, find the last line. There are no guarantees as to the
-- position of the handle after this call, and it is expected that the given
-- handle is seekable.
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)
你可能想在这里加一个减号,因为大多数文件通常以 \n
结尾(而那个空行可能不是你想要的)