使用haskell管道字节串逐行迭代文件



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

更正式地说,我需要将Producer ByteString IO ()转换为Producer String IO (),从而生成行。

我相信对于一个经验丰富的Pipes程序员来说,这一定是一句俏皮话,但到目前为止,我还没有成功破解Pipes ByteString中的所有FreeTLens技巧。

非常感谢您的帮助!

Stephan

如果你需要那种类型的签名,那么我建议你这样做:

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

其中在这种情况下t将是Text:

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

任何时候,如果要减少以FreeT为分隔符的流的每个Producer子组,则可能需要使用purely folds。那么,只需要选择合适的Fold来减少子组。在这种情况下,您只想将一个组中的所有Text块连接起来,因此传入mconcat。我通常不建议这样做,因为它会在非常长的行上中断,但您指定需要这种行为。

之所以如此冗长,是因为pipes生态系统将Text提升到了String之上,并试图鼓励处理任意长的行。如果您不受其他代码的约束,那么更惯用的方法就是:

view (utf8 . lines)

经过一点黑客攻击和这个博客的一些提示,我想出了一个解决方案,但它出奇地笨拙,我担心它也有点低效,因为它使用了ByteString.append:

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)

最新更新