Я ищу, чтобы найти последнее 32-битное слово в двоичном дамбе uInt32, соответствующем определенному шаблону с использованием Haskell. Я могу выполнить задачу, используя last
, однако код должен пройти через весь файл, чтобы он был довольно неэффективным.Haskell: прочитайте двоичный файл назад
Есть ли простой способ сделать readfile
работать через файл в обратном направлении? Я считаю, что это решит проблему с наименьшим изменением текущего кода.
Вот мой текущий код, для справки. Я только начал с Haskell в эти выходные, поэтому я уверен, что это довольно уродливо. Он ищет последнее 32-битное слово, начинающееся с 0b10 в MSB.
import System.Environment(getArgs)
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Internal as BL
import qualified Data.ByteString as BS
import Data.Binary.Get
import Data.Word
import Data.Bits
import Text.Printf(printf)
main = do
args <- getArgs
let file = args!!0
putStrLn $ "Find last 0xCXXXXXXX in " ++ file
content <- BL.readFile file
let packets = getPackets content
putStrLn . show . getValue . last . filterTimes $ packets
-- Data
type Packet = Word32
-- filter where first 2 bits are 10
filterTimes :: [Packet] -> [Packet]
filterTimes = filter ((== 0x2) . tag)
-- get the first 2 bits
tag :: Packet -> Packet
tag rp =
let tagSize = 2
in shiftR rp (finiteBitSize rp - tagSize)
-- remove the tag bits
getValue :: Packet -> Packet
getValue =
let tagSize = 2
mask = complement $ rotateR (2^tagSize - 1) tagSize
in (.&.) mask
-- Input
-- Based on https://hackage.haskell.org/package/binary/docs/Data-Binary-Get.html
getPacket :: Get Packet
getPacket = do
packet <- getWord32le
return $! packet
getPackets :: BL.ByteString -> [Packet]
getPackets input0 = go decoder input0
where
decoder = runGetIncremental getPacket
go :: Decoder Packet -> BL.ByteString -> [Packet]
go (Done leftover _consumed packet) input =
packet : go decoder (BL.chunk leftover input)
go (Partial k) input =
go (k . takeHeadChunk $ input) (dropHeadChunk input)
go (Fail _leftover _consumed msg) _input =
[]
takeHeadChunk :: BL.ByteString -> Maybe BS.ByteString
takeHeadChunk lbs =
case lbs of
(BL.Chunk bs _) -> Just bs
_ -> Nothing
dropHeadChunk :: BL.ByteString -> BL.ByteString
dropHeadChunk lbs =
case lbs of
(BL.Chunk _ lbs') -> lbs'
_ -> BL.Empty
Ну, вы можете использовать '' Handle' с hSeek', прыгать несколько раз от конца файла и прочитать файл в кусках. Правильное выполнение этих кусков будет сложной. – Zeta
С точки зрения эффективности, нет разницы в ожидаемом времени выполнения всех возможных входов; функция, предложенная @Zeta, вероятно, будет медленнее для исходных данных, которые имеют свое последнее согласованное слово в начале. Если у вас нет других знаний по поводу ввода, ваш подход кажется прекрасным. Кроме этого, вы можете использовать поиск назад. Но это может привести к проблемам, зависящим от жесткого диска (поскольку последовательное чтение выполняется быстрее, потому что для жесткого диска не требуется поиск) – SmokeDispenser
Это будет не так мало, как вы хотите, но подумайте об использовании [MMap package] (http://hackage.haskell.org/package/mmap-0.5.9/docs/System-IO-MMap.html), чтобы загружать только часть требуемого файла. –