在Windows上使用Conduit分割线条

时间:2017-01-17 16:45:34

标签: haskell conduit

我在使用管道库来逐行拆分时遇到了一些麻烦。

遗憾的是,与我合作的源数据与行结尾极不一致,在同一文件中同时包含\r\n\n个序列。

我在lines中找到了Data.Conduit.Binary函数,但它已经"分裂"单字节,(\n,足够明显),在某些情况下会留下尾随\r

我理解为什么当前的实现方式与它的工作方式相同,而且我很自信我可以一起破解某种解决方案,但我能想到的唯一方法就是:

lines' = do
   loop $ T.pack ""
   where loop acc = do
         char <- await
         case char of
            Nothing -> return ()
            Just x -> do
            case (isOver $ acc `T.append` x) of
                    (True,y) -> yield y
                    (False,y) -> loop y
                    where isOver n
                           |  (T.takeEnd 2 n == _rLn)  = (True, T.dropEnd 2 n)
                           |  (T.takeEnd 1 n == _Ln)   = (True, T.dropEnd 1 n)
                           |  otherwise                =  (False,n)
                           where _rLn = T.pack $! "\r\n"
                                 _Ln = T.pack $! "\n"

......看起来不那么优雅,很邋and,而且非常慢。

在每次迭代中检查最后两个字符是错误的,因为我真正需要做的就是&#34;记住&#34;如果我读的最后一个字符是\r,但我无法想出一个合理的方法来做到这一点。

有人知道这个问题的更好解决方案吗?

2 个答案:

答案 0 :(得分:4)

不要试图重新发明轮子!我们仍然可以使用conduit-combinators做出更漂亮的东西。作为前言,如果您的\r值永远不会出现,除非有时在换行之前,您可以直接过滤它们。也就是说,我将假设您的案例更为一般 - 您只想摆脱\r之后的\n值。

我们的想法是使用slidingWindowC来获取两个字符块,然后将这些块映射到它们的第一个字符 - 除非字符是"\r\n",在这种情况下我们放弃这两个字符。然后,删除了换行后面的所有\r,我们就可以使用导管linesUnboundedC

{-# LANGUAGE TypeFamilies, FlexibleContexts #-}

import Data.Text (Text, singleton, empty)
import Data.MonoTraversable (Element, MonoFoldable)
import Conduit

main = runConduitRes $ (sourceFile "file.txt" :: Producer (ResourceT IO) Text)
                    .| linesUnboundedC'
                    .| printC

-- | Converted a chunked input of characters into lines delimited by \n or \r\n
linesUnboundedC'
  :: (Element a ~ Char, MonoFoldable a, Monad m) => ConduitM a Text m ()
linesUnboundedC' = concatMapC id
                .| slidingWindowC 2
                .| mapC (\cs@[c,_] -> if cs == "\r\n" then empty else singleton c)
                .| linesUnboundedC

答案 1 :(得分:4)

显然Data.Conduit.Text有一个函数foldLines,其功能正如所描述的那样。