2

私がやろうとしているのは、 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

これで最初の行が表示されますが、実際に必要なのは、一度に各チャンクを改行文字まで効果的に生成することです。それ、どうやったら出来るの?

4

2 に答える 2

3

@マイケルの答えは良いです。ここで行われている使用パターンをいくつか説明したいと思います。

( .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. Lens' (Producer ...) (FreeT (Producer ...) ...)
  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 の関数を使用するには、たとえばon をPB.lines使用してタイプ 1 の関数を取得し、次のように構成します。viewLens'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)
于 2016-06-04T20:27:11.433 に答える
2

pipes-bytestringとは、 a を繰り返し分割すると が生成pipes-groupされるように配置されています。ここでは を意味するように読み取ることができるため、結果は「r を返す一連のバイト文字列プロデューサー セグメント」と考えることができます。このようにして、セグメントの 1 つがたとえば 10 ギガバイトの長さである場合でも、10 ギガバイトの厳密な bytestring ではなく、ストリーミングが行われます。Producer ByteString m rFreeT (Producer ByteString m) m rFreeTA_Succession_Of

改行でバイト文字列プロデューサーを壊したいようですが、改行を保持したいかどうかはわかりませんでした。それらを捨てる場合、これはバイト文字列プロデューサを で分割しview PB.lines、その後に各下位プロデューサを単一の厳密なバイト文字列 (個々の行) に連結するのと同じです。として以下に書きました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

理想的には、各改行で新しいバイト文字列を書き込まないでください。必要があるかどうかは、行で何をしようとしていたかによって異なります。

于 2016-06-04T18:50:43.890 に答える