Haskell 通过在窗上使用导管的线进行拆分

Haskell 通过在窗上使用导管的线进行拆分,haskell,conduit,Haskell,Conduit,我在使用上的导管库按行拆分文本时遇到一些问题 不幸的是,我使用的源数据与行尾极不一致,在同一个文件中同时包含\r\n和\n序列 我在Data.conductor.Binary中找到了行函数,但它被单字节“拆分”(\n,非常合理),这在某些情况下会留下一个尾随\r 我理解当前实现的工作方式,我很有信心我可以一起破解某种解决方案,但我能想到的唯一方法是: lines' = do loop $ T.pack "" where loop acc = do char <

我在使用上的导管库按行拆分文本时遇到一些问题

不幸的是,我使用的源数据与行尾极不一致,在同一个文件中同时包含
\r\n
\n
序列

我在
Data.conductor.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"
行'=do
循环$T.pack“”
其中loop acc=do
字符返回()
只需x->do
案例(超过$acc`T.append`x)
(对,y)->收益率y
(假,y)->循环y
我在哪里
|(T.takeEnd 2n==_rLn)=(真,T.dropEnd 2n)
|(T.takeEnd 1n==_Ln)=(真,T.dropEnd 1n)
|否则=(False,n)
其中_rLn=T.pack$!“\r\n”
_Ln=T.pack$!“\n”
。。。这似乎不雅观,笨拙,而且非常缓慢

在每次迭代中检查最后两个字符感觉是错误的,因为我真正需要做的就是“记住”我读到的最后一个字符是否是
\r
,但我想不出一个明智的方法来做到这一点


有人知道解决这个问题的更好办法吗?

不要试图重新发明轮子!我们仍然可以用它做一些更漂亮的东西。作为前言,如果您的
\r
值除了有时出现在换行符之前之外从未出现过,您可以直接将它们过滤掉。这就是说,我将假设您的情况更一般—您只想去掉后面跟着
\n
\r

这样做的目的是获取两个字符块,然后将这些块映射到它们的第一个字符-除非字符是
“\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

显然,
Data.conductor.Text
有一个功能,
foldLines
,它的工作原理与前面描述的完全一样。

这是一个很棒的答案,非常感谢您提供了有关
滑动窗口的提示,它解决了我还不知道的其他几个问题。然而,显然,
Data.conductor.Text
包中有一个
foldLines
函数,我刚刚发现它可以很好地处理这个用例,所以,请回复:“不要重新发明轮子”我想我会用它作为“正确”答案。@IronGremlin发现得很好!当然可以,它应该快一点。