导管水槽与剩余物

时间:2014-11-09 20:40:09

标签: haskell conduit attoparsec

我有一个接收器,想要在其中使用attoparsec进行一些解析。碰巧我得到Partial结果。所以我认为我可能只是使用leftover将不足的内容放回上游,以便稍后再添加更多内容。但是没有像我希望的那样添加新内容。我非常感谢有关如何解决这个问题的任何建议。谢谢!

{-# LANGUAGE OverloadedStrings #-}

import Control.Monad.IO.Class (liftIO)
import Data.Conduit
import qualified Data.Conduit.List as CL
import qualified Data.ByteString.Char8 as BS
import Data.Attoparsec.Char8


main = (CL.sourceList [BS.pack "foo", BS.pack "bar"]) $$ sink -- endless loop

-- this works:
-- main = (CL.sourceList [BS.pack "foobar"]) $$ sink

sink :: Sink BS.ByteString IO ()
sink = awaitForever $ \str -> do
                  liftIO $ putStrLn $ BS.unpack str -- debug, will print foo forever.
                  case (parse (string "foobar") str) of
                       Fail _ _ _ -> do
                                    liftIO $ putStr $ "f: " ++ BS.unpack str
                                    sink
                       Partial _ -> do
                                    leftover str
                                    sink
                       Done rest final -> do
                                          liftIO $ putStr $ "d: " ++ show final ++ " // " ++ show rest
                                          sink

2 个答案:

答案 0 :(得分:2)

" Partial"是它返回一个延续函数;也就是说,一旦你有更多输入,你就可以用该输入调用continuation。试图将剩余的行重新推送到输入流是浪费,因为你反复解析输入的第一位。

您需要编写函数以将解析器函数作为参数。那么你的部分案例应该是

Partial c -> sink c

这将导致"沉没"等待更多输入,然后将其交给" c"函数,它将继续从中断处解析新输入。

答案 1 :(得分:0)

请记住,Conduit没有连接输出的概念。那么会发生什么:

  • 管道获得部分输入。
  • 它不足以解析。
  • 你把它作为一个剩余的东西。
  • 管道再次读取您放回的内容。
  • 这是永远的。

如果您真的想要追求重复尝试解析器的方向,那么您需要确保每次将剩余的值恢复到比前一次更大的值。所以你要做这样的事情:如果解析器没有完成,请阅读其他输入,将其与您已有的输入连接起来,将其作为剩余部分再次推回并重试。

请注意,上述过程具有复杂性 O(n ^ 2),如果您的解析器在使用大量数据后成功,则会出现特别的问题。如果您一次只能接收一个字符(可能会发生)并且解析器需要消耗1000个字符,那么您将获得500000个处理步骤。所以我强烈建议使用Conduit和Attoparsec之间提供的绑定,或者,如果你想自己做,请正确使用Partial提供的延续。