使用haskell pipes-bytestring逐行迭代文件

时间:2014-09-22 19:58:41

标签: haskell-pipes

我正在使用管道库,需要使用ASCII编码将ByteString流转换为行流(即String)。我知道还有其他库(Pipes.Text和Pipes.Prelude)可能让我更容易从文本文件中产生行,但由于其他一些代码,我需要能够获得行String来自ByteString的制片人。

更正式地说,我需要将Producer ByteString IO ()转换为Producer String IO (),这会产生线条。

我确信这对于经验丰富的管道程序员来说必须是一个单行程序,但到目前为止我还没有成功地破解Pipes-ByteString中的所有FreeTLens - 技巧

非常感谢任何帮助!

斯蒂芬

2 个答案:

答案 0 :(得分:5)

如果您需要该类型签名,那么我建议:

import Control.Foldl (mconcat, purely)
import Data.ByteString (ByteString)
import Data.Text (unpack)
import Lens.Family (view)
import Pipes (Producer, (>->))
import Pipes.Group (folds)
import qualified Pipes.Prelude as Pipes
import Pipes.Text (lines)
import Pipes.Text.Encoding (utf8)
import Prelude hiding (lines)

getLines
    :: Producer ByteString IO r -> Producer String IO (Producer ByteString IO r)
getLines p = purely folds mconcat (view (utf8 . lines) p) >-> Pipes.map unpack

这是有效的,因为purely folds mconcat的类型是:

purely folds mconcat
    :: (Monad m, Monoid t) => FreeT (Producer t m) r -> Producer t m r

...在这种情况下,tText

purely folds mconcat
    :: Monad m => FreeT (Producer Text m) r -> Producer Text m r

如果您想减少Producer分隔的流的每个FreeT子组,您可能希望使用purely folds。然后,只需选择正确的Fold来减少子组。在这种情况下,您只想连接组中的所有Text块,以便传入mconcat。我一般不建议这样做,因为它会在非常长的行上断开,但是你指定你需要这种行为。

这个问题的原因很简单,因为pipes生态系统促使Text超过String并且还试图鼓励处理任意长线。如果您不受其他代码的限制,那么更惯用的方法就是:

view (utf8 . lines)

答案 1 :(得分:1)

经过一些黑客攻击以及blog的一些提示后,我想出了一个解决方案,但它出乎意料地笨拙,我担心它也有点低效,因为它使用了ByteString.append:< / p>

import Pipes
import qualified Pipes.ByteString as PB
import qualified Pipes.Prelude as PP
import qualified Pipes.Group as PG
import qualified Data.ByteString.Char8 as B
import Lens.Family (view )
import Control.Monad (liftM)

getLines :: Producer PB.ByteString IO r -> Producer String IO r
getLines = PG.concats . PG.maps toStringProducer . view PB.lines

toStringProducer :: Producer PB.ByteString IO r -> Producer String IO r
toStringProducer producer = go producer B.empty
  where
    go producer bs = do
        x <- lift $ next producer
        case x of
            Left r -> do
                yield $ B.unpack bs
                return r
            Right (bs', producer') -> go producer' (B.append bs' bs)