Я пытаюсь разбора двоичных данных с использованием pipe-attoparsec в Haskell. Причина, по которой используются трубы (прокси), - это чередование чтения с помощью синтаксического анализа, чтобы избежать использования большой памяти для больших файлов. Многие двоичные форматы основаны на блоках (или кусках), а их размеры часто описываются полем в файле. Я не уверен, что называется парсер для такого блока, но это то, что я подразумеваю под «подпарасером» в названии. Проблема заключается в том, чтобы реализовать их в сжатом виде без потенциально большого объема памяти. Я придумал две альтернативы, каждая из которых терпит неудачу.«Sub-parsers» in pipes-attoparsec
Альтернатива 1 заключается в том, чтобы прочитать блок в отдельную строку и запустить для нее отдельный синтаксический анализатор. Хотя краткий, большой блок вызовет высокую память.
Альтернатива 2 заключается в том, чтобы продолжать разбор в одном контексте и отслеживать количество потребляемых байтов. Это отслеживание подвержено ошибкам и, похоже, заражает все синтаксические анализаторы, которые формируются в последнем блочном процессоре. Для искаженного входного файла он также может тратить время, анализируя дальше, чем указано полем размера, прежде чем можно будет сравнить отслеживаемый размер.
import Control.Proxy.Attoparsec
import Control.Proxy.Trans.Either
import Data.Attoparsec as P
import Data.Attoparsec.Binary
import qualified Data.ByteString as BS
parser = do
size <- fromIntegral <$> anyWord32le
-- alternative 1 (ignore the Either for simplicity):
Right result <- parseOnly blockParser <$> P.take size
return result
-- alternative 2
(result, trackedSize) <- blockparser
when (size /= trackedSize) $ fail "size mismatch"
return result
blockParser = undefined
main = withBinaryFile "bin" ReadMode go where
go h = fmap print . runProxy . runEitherK $ session h
session h = printD <-< parserD parser <-< throwParsingErrors <-< parserInputD <-< readChunk h 128
readChunk h n() = runIdentityP go where
go = do
c <- lift $ BS.hGet h n
unless (BS.null c) $ respond c *> go
Вставка трубы, которая подсчитывает восходящий поток, звучит интересно, но как она будет знать, сколько байтов считать? Это значение обнаруживается только парсером вниз по течению, который не может напрямую вызвать запрос со значением как параметром, поскольку он выполняется парсером. – absence
@absence Ну, теперь игнорируйте интерфейс pipe-attoparsec, потому что мы с Ренцо скоро исправим его. Фиксированный входной парсер внутренне использует канал, который ограничивает количество байтов. Подумайте об этом как: 'parser1 >> (ограничивайте n> -> parser2) >> parser3'. Комбинатор с фиксированной шириной вставляет что-то вроде 'ограничивает 'выше указанного парсера. Это сложнее, чем это, но довольно похоже на дух. –
Ссылки мертвы – SwiftsNamesake