Subscribed unsubscribe Subscribe Subscribe

賢いお尻の舐め方

Haskell

でかいファイルの最後の行を取り出したいときにどうすればよいか。Cだったらmmapしてお尻から舐めていけばいい?けど、標準ライブラリにmmapもないのであんまりうれしくない。seekで頑張るならこんな感じか。

import Control.Exception
import Control.Monad.Fix
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy.Char8 as L
import System.Exit
import System.Environment
import System.IO
import Prelude hiding (catch)

tail1 :: FilePath -> Int -> IO ByteString
tail1 path initOffset = do
  bracket (openFile path ReadMode) hClose $ \h ->
    flip fix initOffset $ \loop offset -> do
      newOffset <- flip fix offset $ \seek offset' -> do
        hSeek h SeekFromEnd (- (fromIntegral offset')) >> return offset'
        `catch` \_ -> seek (offset' `div` 2)
      bytes <- L.hGet h newOffset
      case L.elemIndices '\n' bytes of
        []  -> loop (newOffset * 2)
        xs -> do let tailOffset = newOffset - fromIntegral (last xs) - 1
                 hSeek h SeekFromEnd (- (fromIntegral tailOffset))
                 L.hGet h tailOffset
  `catch` \e -> print e >> exitWith (ExitFailure 1)

あまり賢い感じがしない。もっと賢くお尻を舐めたい。