2014-09-22 9 views
5

Ich verwende die Pipes-Bibliothek und muss einen ByteString-Stream in einen Strom von Zeilen konvertieren (d. H. String), ASCII-Codierung verwenden. Ich bin mir bewusst, dass es andere Bibliotheken (Pipes.Text und Pipes.Prelude) gibt, die mir vielleicht Zeilen aus einer Textdatei leichter ergeben, aber wegen etwas anderem Code muss ich Linien wie String von einem Produzenten von bekommen können ByteString.Verwenden von Haskell-Pipes-Bytestring zum Iterieren einer Datei nach Zeile

Formaler muss ich eine Producer ByteString IO() in eine konvertieren, die Linien ergibt.

Ich bin sicher, dass dies ein One-Liner für einen erfahrenen Pipes-Programmierer sein muss, aber ich schaffte es bisher nicht erfolgreich durch alle FreeT und Lens-Trocker in Pipes-ByteString zu hacken.

Jede Hilfe wird sehr geschätzt!

Stephan

+0

'ausgekleideten :: Monad m => Produzent ByteString mr -> Produzent String m (Produzent ByteString mr)' 'gefüttert = Falten mappend mempty T.unpack. Sehen Sie sich PT.lines an. PTE.decodeAscii'. Wenn Sie mögliche nicht lesbare Bytestring-Reste löschen möchten, fügen Sie 'Control.Monad.void' hinzu. – Michael

Antwort

5

Wenn Sie diese Art Signatur benötigen, dann würde ich dies vorschlagen ist:

purely folds mconcat 
    :: (Monad m, Monoid t) => FreeT (Producer t m) r -> Producer t m r 

... wo t in diesem Fall Text würde:

purely folds mconcat 
    :: Monad m => FreeT (Producer Text m) r -> Producer Text m r 

Jedes Mal, wenn jede Producer Untergruppe eines FreeT separierten Stream möchten Sie wahrscheinlich reduzieren verwenden möchten purely folds. Dann ist es nur eine Frage der richtigen Fold, um die Untergruppe mit zu reduzieren. In diesem Fall möchten Sie nur alle Chunks innerhalb einer Gruppe verketten, so dass Sie mconcat übergeben. Im Allgemeinen empfehle ich das nicht, da es bei extrem langen Zeilen unterbrochen wird, aber Sie haben angegeben, dass Sie dieses Verhalten benötigen.

Der Grund, warum dies ausführlich ist, ist, weil das pipes Ökosystem über String fördert und auch versucht, die Handhabung beliebig langer Zeilen zu fördern. Wenn Sie nicht von Ihrem anderen Code gezwungen wurden dann die mehr idiomatische Ansatz wäre nur:

view (utf8 . lines) 
+0

Große Antwort, Danke! Auch hilfreich für das Verständnis der Linsen in dieser Bibliothek. – Stephan

+0

Gern geschehen! –

1

Nach ein wenig von Hacking und einige Hinweise aus diesem blog, kam ich mit einer Lösung, aber es ist überraschend ungeschickt, und ich fürchte ein wenig ineffizient als auch, wie es ByteString.append verwendet :

import Control.Foldl (mconcat, purely) 
import Data.ByteString (ByteString) 
import Data.Text (unpack) 
import Lens.Family (view) 
import Pipes (Producer, (>->)) 
import Pipes.Group (folds) 
import qualified Pipes.Prelude as Pipes 
import Pipes.Text (lines) 
import Pipes.Text.Encoding (utf8) 
import Prelude hiding (lines) 

getLines 
    :: Producer ByteString IO r -> Producer String IO (Producer ByteString IO r) 
getLines p = purely folds mconcat (view (utf8 . lines) p) >-> Pipes.map unpack 

das funktioniert, weil die Art der purely folds mconcat:

import Pipes 
import qualified Pipes.ByteString as PB 
import qualified Pipes.Prelude as PP 
import qualified Pipes.Group as PG 
import qualified Data.ByteString.Char8 as B 
import Lens.Family (view) 
import Control.Monad (liftM) 

getLines :: Producer PB.ByteString IO r -> Producer String IO r 
getLines = PG.concats . PG.maps toStringProducer . view PB.lines 

toStringProducer :: Producer PB.ByteString IO r -> Producer String IO r 
toStringProducer producer = go producer B.empty 
    where 
    go producer bs = do 
     x <- lift $ next producer 
     case x of 
      Left r -> do 
       yield $ B.unpack bs 
       return r 
      Right (bs', producer') -> go producer' (B.append bs' bs)