Использование haskell pipe-bytestring для итерации файла за строкой

Я использую библиотеку каналов и мне нужно преобразовать поток ByteString в поток строк (т.е. String), используя кодировку ASCII. Я знаю, что есть другие библиотеки (Pipes.Text и Pipes.Prelude), которые, возможно, позволяют мне легче выводить строки из текстового файла, но из-за некоторого другого кода мне нужно иметь возможность получать строки как String от производителя ByteString,

Более формально мне нужно преобразовать Producer ByteString IO () к Producer String IO (), что дает строки.

Я уверен, что это должно быть одной строкой для опытного программиста Pipes, но мне пока не удалось успешно взломать все FreeT а также Lensхитрость в Pipes-ByteString.

Любая помощь высоко ценится!

Stephan

2 ответа

Решение

Если вам нужна подпись этого типа, я бы предложил это:

import Control.Foldl (mconcat, purely)
import Data.ByteString (ByteString)
import Data.Text (unpack)
import Lens.Family (view)
import Pipes (Producer, (>->))
import Pipes.Group (folds)
import qualified Pipes.Prelude as Pipes
import Pipes.Text (lines)
import Pipes.Text.Encoding (utf8)
import Prelude hiding (lines)

getLines
    :: Producer ByteString IO r -> Producer String IO (Producer ByteString IO r)
getLines p = purely folds mconcat (view (utf8 . lines) p) >-> Pipes.map unpack

Это работает, потому что тип purely folds mconcat является:

purely folds mconcat
    :: (Monad m, Monoid t) => FreeT (Producer t m) r -> Producer t m r

... где t в этом случае будет Text:

purely folds mconcat
    :: Monad m => FreeT (Producer Text m) r -> Producer Text m r

В любое время вы хотите уменьшить каждый Producer подгруппа FreeT-ограниченный поток, который вы, вероятно, хотите использовать purely folds, Тогда это просто вопрос выбора правильного Fold сократить подгруппу с. В этом случае вы просто хотите объединить все Text куски в группе, так что вы переходите в mconcat, Как правило, я не рекомендую делать это, поскольку он будет разбиваться на очень длинные строки, но вы указали, что вам нужно такое поведение.

Причина этого многословна, потому что pipes экосистема способствует Text над String а также пытается поощрять обработку произвольно длинных строк. Если бы вы не были ограничены вашим другим кодом, то более идиоматический подход был бы следующим:

view (utf8 . lines)

После небольшого взлома и некоторых подсказок из этого блога я придумал решение, но оно на удивление неуклюжее, и я также боюсь немного неэффективно, так как оно использует ByteString.append:

import Pipes
import qualified Pipes.ByteString as PB
import qualified Pipes.Prelude as PP
import qualified Pipes.Group as PG
import qualified Data.ByteString.Char8 as B
import Lens.Family (view )
import Control.Monad (liftM)

getLines :: Producer PB.ByteString IO r -> Producer String IO r
getLines = PG.concats . PG.maps toStringProducer . view PB.lines

toStringProducer :: Producer PB.ByteString IO r -> Producer String IO r
toStringProducer producer = go producer B.empty
  where
    go producer bs = do
        x <- lift $ next producer
        case x of
            Left r -> do
                yield $ B.unpack bs
                return r
            Right (bs', producer') -> go producer' (B.append bs' bs)
Другие вопросы по тегам