如何从管道中读取不同的块(例如直到LF然后10个字节的行)?

时间:2013-07-29 15:39:24

标签: haskell input conduit

对于一个网络协议,我需要能够灵活地从Source m ByteString读取不同类型的块。有lines组合子,它将输入分成线,但我需要能够组合读取线和固定数量的字节。

我目前的方法是创建一个辅助函数:

  

|在输入上折叠给定的函数。函数返回时重复Left   并将其结果累积在一个列表中。当函数返回Right时,   连接累计结果(包括最后一个)并返回它,   使用leftover存储剩下的内容。如果没有输入,则返回Nothing   可用。

chunk :: (Monad m, Monoid a)
      => (s -> i -> Either (a, s) (a, i))
      -> s
      -> Consumer i m (Maybe a)
chunk f = loop []
  where
    loop xs s = await >>= maybe (emit xs) (go xs s)

    go xs s i = case f s i of
        Left (x, s')    -> loop (x : xs) s'
        Right (x, l)    -> leftover l >> emit (x : xs)

    emit [] = return Nothing
    emit xs = return (Just . mconcat . L.reverse $ xs)
-- Note: We could use `mappend` to combine the chunks directly. But this would
-- often get us O(n^2) complexity (like for `ByteString`s) so we keep a list of
-- the chunks and then use `mconcat`, which can be optimized by the `Monoid`.

使用此功能,我创建了特定的消费者:

bytes :: (Monad m) => Int -> Consumer ByteString m (Maybe ByteString)
bytes = chunk f
  where
    f n bs | n' > 0     = Left (bs, n')
           | otherwise  = Right $ BS.splitAt n bs
      where n' = n - BS.length bs

line :: (Monad m) => Consumer ByteString m (Maybe ByteString)
line = chunk f ()
  where
    f _ bs = maybe (Left (bs, ()))
                   (\i -> Right $ BS.splitAt (i + 1) bs)
                   (BS.findIndex (== '\n') bs)

有更好的方法吗?我想这个问题肯定已经解决了。

1 个答案:

答案 0 :(得分:1)

看起来正确,它与Warp解析请求标头的方式非常相似,但Warp并不打扰任何更高级别的组合器。