【问题标题】:Filtering ANSI escape sequences from a ByteString with Conduit使用 Conduit 从 ByteString 过滤 ANSI 转义序列
【发布时间】:2016-12-08 01:26:54
【问题描述】:

我正在尝试制作一个从 ByteStrings 过滤 ANSI 转义码的导管。我想出了一个函数,将 ByteString 转换为 Word8 的流,进行过滤,最后再转换回 ByteStream 流。

当我在 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的过滤内容,但没有回显。

但是,如果我在 doConversionAndPrint 中注释掉 ansiFilter,回显确实有效,这让我觉得 ansiFilter 函数是错误的。

任何帮助将不胜感激!

【问题讨论】:

  • 请将您正在使用的导入添加到代码中,以便我们可以更直接地运行它。
  • toByteString 不是将整个Word8s 流变成一个严格的字节串吗?如果是这样,ansiFilter 似乎永远不会完成。
  • 例如,如果我只写toByteString = mapC BS.singleton 它是有效率的,虽然标准输入和标准输出的干扰有点难以理解。
  • @duplode 我编辑了我的问题以包含导入。对此感到抱歉。

标签: haskell conduit


【解决方案1】:

我根据管道组合器中更高级别的分块数据功能重新实现了ansiFilter,例如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

【讨论】:

    【解决方案2】:

    采用了稍微不同的方法,并且更幸运地不使用 ByteStrings。我认为这放弃了一些流媒体的东西,但对于我的用例来说是可以接受的。

    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)
    

    【讨论】:

      猜你喜欢
      • 2012-04-14
      • 2010-11-25
      • 2011-06-18
      • 2011-09-27
      • 1970-01-01
      • 2014-06-15
      • 2021-06-30
      • 2015-08-14
      • 1970-01-01
      相关资源
      最近更新 更多