[英]haskell pipes - how to repeatedly perform a takeWhile operation on a bytestring pipe?
我想做的是使用takeWhile按某個字符分割字節串。
import qualified Data.ByteString.Internal as BS (c2w, w2c)
import Pipes
import Pipes.ByteString as PB
import Pipes.GZip
import Pipes.Prelude as PP
import System.IO
newline = BS.c2w '\n'
splitter = PB.takeWhile (\myWord -> myWord /= newline)
myPipe fileHandle = PP.toListM $ decompress fileProducer >-> splitter
where
fileProducer = PB.fromHandle fileHandle
run = do
dat <- withFile "somefile.blob" ReadMode myPipe
pure dat
這使我獲得了第一行,但我真正想要的是一次有效地將每個塊轉換為換行符。 我怎么做?
@Michael的答案很好。 我只想說明這里發生的一些使用模式。
(.lhs可在http://lpaste.net/165352上獲得 )
前幾個進口:
{-# LANGUAGE OverloadedStrings, NoMonomorphismRestriction #-}
import Pipes
import qualified Pipes.Prelude as PP
import qualified Pipes.Group as PG
import qualified Pipes.ByteString as PB
import qualified Pipes.GZip as GZip
import qualified Data.ByteString as BS
import Lens.Family (view, over)
import Control.Monad
import System.IO
如果查看Pipes.ByteString和Pipes.GZip中的函數,您會發現它們全部都屬於以下類型模式:
每個類別中的功能示例:
PB.words
PG.concats
PB.lines
, PB.chunksOf
, PB.splits
,... GZip.compress
, GZip.decompress
這是使用PB.words
將輸入流拆分為單詞的方法:
prod = yield "this is\na test\nof the pipes\nprocessing\nsystem"
t1 = runEffect $ (PG.concats . PB.words) prod >-> PP.print
要使用類型3的功能-例如PB.lines
,只需在Lens'
上使用view
即可獲取類型1的功能,然后與PG.concats
組合:
t2a = runEffect $ (PG.concats . view PB.lines) prod >-> PP.print
t2b h = (PG.concats . view PB.lines) (PB.fromHandle h) >-> PP.print
run2 = withFile "input" ReadMode (runEffect . t2b)
對於Producer-> Producer函數,只需使用常規函數應用程序即可:
t3 h = GZip.decompress (PB.fromHandle h) >-> PP.print
run3 = withFile "input.gz" ReadMode (runEffect . t3)
t4 h = GZip.decompress (PB.fromHandle h) >-> PP.map BS.length >-> PP.print
run4 = withFile "big.gz" ReadMode (runEffect . t4)
首先解壓縮然后按行分割,我們嵌套函數應用程序:
t5 h = (PG.concats . view PB.lines) ( GZip.decompress (PB.fromHandle h) )
>-> PP.map BS.length >-> PP.print
run5 = withFile "input.gz" ReadMode (runEffect . t5)
pipes-bytestring
和pipes-group
被布置為使得多次開斷一個Producer ByteString mr
產生一個FreeT (Producer ByteString m) mr
。 FreeT
在這里可以理解為A_Succession_Of
,因此結果可以被認為是“一系列返回r的字節串生成器段的A_Succession_Of
”。 這樣,如果其中一個段的長度為10吉字節 ,那么我們仍然有流式傳輸,而不是10 GB的嚴格字節串 。
在我看來,您想中斷換行符中的字節串生成器,但是我無法確定您是否要保留換行符。 如果要扔掉它們,這與使用view PB.lines
分割字節view PB.lines
生產者view PB.lines
,然后將每個從屬生產者串聯為一個嚴格的字節串-單獨的行。 我在下面將accumLines
寫為accumLines
。 它很簡單,但是很少使用Lens.view
將花哨的PB.lines
鏡頭變成常規功能。 (許多操作被寫為pipes-bytestring
鏡頭,因為這樣它們可以重新用於其他目的,尤其是生產者解析pipes
喜歡的那種。)
import Pipes
import qualified Pipes.Prelude as P
import Pipes.ByteString as PB
import qualified Pipes.Group as PG
import Pipes.GZip
import qualified Data.ByteString.Internal as BS (c2w, w2c)
import System.IO
import Lens.Simple (view) -- or Control.Lens or whatever
import Data.Monoid
main = run >>= mapM_ print
myPipe fileHandle = P.toListM $ accumLines (decompress fileProducer)
where
fileProducer = PB.fromHandle fileHandle
run = do
dat <- withFile "a.gz" ReadMode myPipe
pure dat
-- little library additions
accumLines :: Monad m => Producer ByteString m r -> Producer ByteString m r
accumLines = mconcats . view PB.lines
accumSplits :: Monad m => Char -> Producer ByteString m r -> Producer ByteString m r
accumSplits c = mconcats . view (PB.splits (BS.c2w c))
-- this is convenient, but the operations above could
-- be more rationally implemented using e.g. BL.fromChunks and toListM
mconcats :: (Monad m, Monoid b) => FreeT (Producer b m) m r -> Producer b m r
mconcats = PG.folds (<>) mempty id
理想情況下,您不必在每次換行時都寫一個新的字節串。 是否必須取決於對這些行的處理方式。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.