Использование 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)