2016-12-08 6 views
5

ByteStringsからANSIエスケープコードをフィルタリングするコンジットを作成しようとしています。 ByteStringをWord8のストリームに変換し、フィルタリングし、最後にByteStreamのストリームに変換する関数を思いついた。コンジット付きByteStringからANSIエスケープシーケンスをフィルタリングする

私がGHCiの中でそれを使用するときに正常に動作するようです:

> runConduit $ yield "hello\27[23;1m world" .| ansiFilter .| printC 
"hello world" 

私は自分のアプリケーションで使用する場合、ansiFilterが含まれている導管が通過何かを渡していないように見えます。ここでは完全なソースは、次のとおりです。

{-# LANGUAGE OverloadedStrings #-} 

module Main where 

import Conduit 
import Control.Concurrent.Async 
import Control.Concurrent.STM 
import Data.ByteString (ByteString) 
import qualified Data.ByteString as BS 
import Data.Conduit.TQueue 
import Data.Word8 (Word8) 
import qualified Data.Word8 as Word8 

main :: IO() 
main = do 

     queue <- atomically $ newTBQueue 25 
     let qSource = sourceTBQueue queue 
     atomically $ writeTBQueue queue ("hello" :: ByteString) 

     race_ 
     (putInputIntoQueue queue) 
     (doConversionAndPrint qSource) 

putInputIntoQueue q = 
    runConduit 
    $ stdinC 
    .| iterMC (atomically . writeTBQueue q) 
    .| sinkNull 

doConversionAndPrint src = 
    runConduit 
    $ src 
    .| ansiFilter 
    .| stdoutC 

ansiFilter :: MonadIO m => ConduitM ByteString ByteString m() 
ansiFilter = toWord8 .| ansiFilter' .| toByteString 
    where 
    ansiFilter' = awaitForever $ \first -> do 
     msecond <- peekC 
     case (first, msecond) of 
     (0x1b, Just 0x5b) -> do 
      dropWhileC (not . Word8.isLetter) 
      dropC 1 
     _ -> yield first 

    toWord8 = concatC 

    toByteString :: Monad m => ConduitM Word8 ByteString m() 
    toByteString = 
     (mapC BS.singleton .| foldC) >>= yield 

このプログラムはstdinのフィルタ処理内容をエコーバックすることになっているが、何もエコーバックなかっます。

ansiFilterdoConversionAndPrintにコメントアウトすると、エコーが働いてansiFilter機能が間違ってしまう事があります。

ご協力いただければ幸いです!

+1

、コードに使用している輸入品を追加してください。 – duplode

+2

'toByteString'は' Word8'のストリーム全体を単一の厳密なバイトコードに変換していませんか?もしそうなら、 'ansiFilter'は決して終わらないはずです。 – Michael

+2

たとえば、 'toByteString = mapC BS.singleton'と書くと、stdinとstdoutの干渉は少し遅れますが、生産的です。 – Michael

答えて

2

takeWhileCEように、導管-コンビネータでデータ機能をチャンク。これが動作しているようですし、効率的なメモリ表現に残っているデータのより多くをさせることで、より効率的にする必要があります:私たちはもっと素直にそれを実行できるように

ansiFilter :: MonadIO m => ConduitM ByteString ByteString m() 
ansiFilter = loop 
    where 
    loop = do 
     takeWhileCE (/= 0x1b) 
     mfirst <- headCE 
     case mfirst of 
     Nothing -> return() 
     Just first -> assert (first == 0x1b) $ do 
      msecond <- peekCE 
      case msecond of 
      Just 0x5b -> do 
       dropWhileCE (not . Word8.isLetter) 
       dropCE 1 
      _ -> yield $ BS.singleton first 
      loop 
1

少し違ったアプローチで行きましたが、ByteStringsだけを残して運が増えました。私はこれがストリーミングのものをあきらめていると思っていますが、私の使用事例では受け入れられます。私はより高いレベルの点でansiFilterを再実装

ansiFilter :: Monad m => Conduit ByteString m ByteString 
ansiFilter = mapC (go "") 
    where 
    csi = "\27[" 
    go acc "" = acc 
    go acc remaining = go (acc <> filtered) (stripCode unfiltered) 
     where 
     (filtered, unfiltered) = BS.breakSubstring csi remaining 
     stripCode bs = BS.drop 1 (BS.dropWhile (not . Word8.isLetter) bs) 
関連する問題