2017-10-27 7 views
2

Ich möchte Conduit in einer Einstellung verwenden, wo ich eine Binärdatei lesen, überprüfen Sie, dass es den richtigen Header hat, und dann an den verbleibenden Daten in der Datei arbeiten.Conduit, die Dateikopf überprüft

Wenn Sie versuchen, einen Conduit zu schreiben, der den Header prüft und dann den Rest der Daten an die folgenden Conduits überträgt, stoße ich in Schwierigkeiten. Ich habe sie leben in einem Either String Monad für einige Ausnahmen. Hier ist eine vereinfachte Version des Codes (ich bin mir bewusst, es gibt ein Condiut.Attoparsec Modul, aber jetzt würde Ich mag es schreiben selbst):

import Conduit (ConduitM, mapC, mapM_C, takeWhileCE) 
import Data.ByteString.Conversion (toByteString') 

separator :: ByteString 
separator = toByteString' '#' 

check :: ByteString -> Either String() 

confirmHeader :: ConduitM ByteString ByteString (Either String)() 
confirmHeader = do 
    takeWhileC (/= separator) .| mapM_C check 
    mapC id 

separator ist eine vordefinierte ByteString, die das Ende des Headers signalisiert. Die Zeile mapC id soll den Rest des Streams weiterleiten, wenn der Header auscheckt. Ich habe die unwichtigen Details von check weggelassen.

Der Teil, der die Kopfzeile überprüft, funktioniert. Die letzte Zeile, abgesehen von unelegant und nicht-idiomatisch, funktioniert nicht. so etwas wie

runConduit $ yield (toByteString' "header#rest") .| confirmHeader .| sinkList 

Lauf Gibt Right [] statt Right ["rest"], wie ich gehofft hatte. Irgendwelche Ideen?

Antwort

1

Ihre takeWhileC (/= separator) nimmt das ganze ByteString: es funktioniert nicht auf Brocken von ByteString s! Sie können Data.Conduit.Binary verwenden, um an einzelnen Bytes des Streams zu arbeiten. Der folgende Code funktioniert "wie erwartet", glaube ich.

module Main (main) where 

import   Conduit 
import   Data.ByteString (ByteString) 
import   Data.ByteString.Conversion (toByteString') 
import   Data.Char (ord) 
import qualified Data.Conduit.Binary as B 
import   GHC.Word (Word8) 

separator :: Word8 
separator = toEnum $ ord '#' 

check :: ByteString -> Either String() 
check _ = Right() 

confirmHeader :: ConduitM ByteString ByteString (Either String)() 
confirmHeader = do 
    B.takeWhile (/= separator) .| mapM_C check 
    B.drop 1 -- drop separator which stayed in stream 
    mapC id 

main :: IO() 
main = print . runConduit $ 
    yield (toByteString' "header#rest") .| confirmHeader .| sinkList 

Und die Ausgabe:

[nix-shell:/tmp]$ ghc C.hs -fforce-recomp -Wall -Werror -o Main && ./Main 
[1 of 1] Compiling Main    (C.hs, C.o) 
Linking Main ... 
Right ["rest"] 
+0

Great! Als kleines Detail habe ich mich für takeWhileCE entschieden, aber ich werde es mit Conduit.Binary vergleichen. – jorgen

+0

'takeWhileCE' sollte funktionieren: Eigentlich wollte ich das verwenden, aber ich habe seinen Namen vergessen und Conduit.Binary kam zuerst auf Hacker. –

+0

Ok! Kannst du dir übrigens etwas eleganteres vorstellen als 'mapC id', oder ist das Standard? Entschuldigung, wenn dies jetzt aus dem Thema ist .. – jorgen

Verwandte Themen