我在使用管道库来逐行拆分时遇到了一些麻烦。
遗憾的是,与我合作的源数据与行结尾极不一致,在同一文件中同时包含\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
,但我无法想出一个合理的方法来做到这一点。
有人知道这个问题的更好解决方案吗?
答案 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
,其功能正如所描述的那样。