簡體   English   中英

haskell管道-如何在字節串管道上重復執行takeWhile操作?

[英]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中的函數,您會發現它們全部都屬於以下類型模式:

  1. 生產者...-> FreeT(生產者...)...
  2. FreeT(生產者...)...->生產者...
  3. 鏡頭(制作人...)(FreeT(制作人...)...)
  4. 生產者...->生產者...

每個類別中的功能示例:

  1. PB.words
  2. PG.concats
  3. PB.linesPB.chunksOfPB.splits ,...
  4. GZip.compressGZip.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-bytestringpipes-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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM