我正在使用管道库,需要使用ASCII编码将ByteString流转换为行流(即String
)。我知道还有其他库(Pipes.Text和Pipes.Prelude)可能让我更容易从文本文件中产生行,但由于其他一些代码,我需要能够获得行String
来自ByteString
的制片人。
更正式地说,我需要将Producer ByteString IO ()
转换为Producer String IO ()
,这会产生线条。
我确信这对于经验丰富的管道程序员来说必须是一个单行程序,但到目前为止我还没有成功地破解Pipes-ByteString中的所有FreeT
和Lens
- 技巧
非常感谢任何帮助!
斯蒂芬
答案 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
...在这种情况下,t
为Text
:
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)