Ich versuche, Binärdaten mit Pipes-Attoparsec in Haskell zu analysieren. Der Grund dafür, dass Pipes (Proxies) beteiligt sind, besteht darin, das Lesen mit Parsing zu verschachteln, um eine hohe Speicherbenutzung für große Dateien zu vermeiden. Viele Binärformate basieren auf Blöcken (oder Chunks), und ihre Größen werden oft durch ein Feld in der Datei beschrieben. Ich bin mir nicht sicher, was ein Parser für einen solchen Block heißt, aber das meine ich mit "Sub-Parser" im Titel. Das Problem, das ich habe, ist, sie in einer prägnanten Weise ohne einen potenziell großen Speicherbedarf zu implementieren. Ich habe mir zwei Alternativen ausgedacht, die beide in gewisser Hinsicht versagen."Sub-Parser" in Pipes-Attoparsec
Alternative 1 besteht darin, den Block in eine separate Bytefolge zu lesen und einen separaten Parser dafür zu starten. Kurz gesagt, ein großer Block führt zu einem hohen Speicherverbrauch.
Alternative 2 besteht darin, im selben Kontext zu analysieren und die Anzahl der verbrauchten Bytes zu verfolgen. Dieses Tracking ist fehleranfällig und scheint alle Parser zu befallen, die in den finalen blockParser fallen. Bei einer fehlerhaften Eingabedatei könnte sie auch Zeit durch Parsing vergeuden, die weiter ist als durch das Größenfeld angegeben, bevor die verfolgte Größe verglichen werden kann.
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
Das Einfügen einer Pipe, die Upstream zählt, klingt interessant, aber woher weiß es, wie viele Bytes gezählt werden sollen? Dieser Wert wird nur vom nachgeschalteten Parser erkannt, der die Anfrage nicht direkt mit dem Wert als Parameter aufrufen kann, da er von parserD ausgeführt wird. – absence
@absence Nun, ignorieren Sie die Pipes-attoparsec-Schnittstelle für jetzt, weil Renzo und ich es bald beheben werden. Der feste Eingabe-Parser verwendet intern eine Pipe, die die Byteanzahl einschränkt. Stellen Sie sich folgendes vor: 'parser1 >> (restrict n> -> parser2) >> parser3'. Die Kombinatorik mit fester Breite fügt etwas wie "Beschränken" stromaufwärts von dem gegebenen Parser ein. Es ist komplizierter, aber im Geist ziemlich ähnlich. –
Die Links sind tot – SwiftsNamesake