Haskell / Conduit: чтение файла построчно - PullRequest
1 голос
/ 05 июня 2019

Сценарий : у меня есть текстовый файл ~ 900 МБ, который отформатирован следующим образом

...
Id:   109101
ASIN: 0806978473
  title: The Beginner's Guide to Tai Chi
  group: Book
  salesrank: 672264
  similar: 0
  categories: 3
   |Books[283155]|Subjects[1000]|Sports[26]|Individual Sports[16533]|Martial Arts[16571]|General[16575]
   |Books[283155]|Subjects[1000]|Sports[26]|Individual Sports[16533]|Martial Arts[16571]|Taichi[16583]
   |Books[283155]|Subjects[1000]|Sports[26]|General[11086921]
  reviews: total: 2  downloaded: 2  avg rating: 5
    2000-4-4  cutomer: A191SV1V1MK490  rating: 5  votes:   0  helpful:   0
    2004-7-10  cutomer:  AVXBUEPNVLZVC  rating: 5  votes:   0  helpful:   0
                    (----- empty line ------)    
Id :

и хотите проанализировать информацию из него.

Проблема : В качестве первого шага (и поскольку он мне нужен для другого контекста) я хочу построчно обрабатывать файл, а затем собирать вместе «куски», принадлежащие одному продукту, а затем обрабатывать их отдельно с другой логикой.

Итак, план следующий:

  1. Определить источник, представляющий текстовый файл
  2. Определите канал (?), Который берет по одной линии от этого источника и ...
  3. ... передает его некоторым другим компонентам.

Теперь я пытаюсь адаптировать следующий пример:

doStuff = do
  writeFile "input.txt" "This is a \n test." -- Filepath -> String -> IO ()

  runConduitRes                  -- m r
    $ sourceFileBS "input.txt"   -- ConduitT i ByteString m ()  -- by "chunk"
    .| sinkFile "output.txt"     -- FilePath -> ConduitT ByteString o m ()

  readFile "output.txt"
    >>= putStrLn 

То есть sourceFileBS "input.txt" имеет тип ConduitT i ByteString m (), то есть канал с

  • тип ввода i
  • тип выхода ByteStream
  • тип монады t
  • тип результата ().

sinkFile направляет все входящие данные в данный файл. sinkFile "output.txt" - это канал с типом ввода ByteStream.

Теперь мне нужно построчно обрабатывать входной источник, то есть передавать только одну строку в каждом последующем. В псевдокоде:

sourceFile "input.txt"
splitIntoLines
yieldMany (?)
other stuff

Как мне это сделать?

Что у меня сейчас есть

copyFile = do
  writeFile "input.txt" "This is a \n test." -- Filepath -> String -> IO ()

  runConduitRes                  -- m r
    (lineC $ sourceFileBS "input.txt")   -- ConduitT i ByteString m ()  -- by "chunk"
    .| sinkFile "output.txt"     -- FilePath -> ConduitT ByteString o m ()

  readFile "output.txt"
    >>= putStrLn --

но это приводит к следующей ошибке типа:

    * Couldn't match type `bytestring-0.10.8.2:Data.ByteString.Internal.ByteString'
                     with `Void'
      Expected type: ConduitT
                       ()
                       Void
                       (ResourceT
                          (ConduitT
                             a0 bytestring-0.10.8.2:Data.ByteString.Internal.ByteString m0))
                       ()
        Actual type: ConduitT
                       ()
                       bytestring-0.10.8.2:Data.ByteString.Internal.ByteString
                       (ResourceT
                          (ConduitT
                             a0 bytestring-0.10.8.2:Data.ByteString.Internal.ByteString m0))
                       ()
    * In the first argument of `runConduitRes', namely
        `(lineC $ sourceFileBS "input.txt")'
      In the first argument of `(.|)', namely
        `runConduitRes (lineC $ sourceFileBS "input.txt")'
      In a stmt of a 'do' block:
        runConduitRes (lineC $ sourceFileBS "input.txt")
          .| sinkFile "output.txt"
   |
28 |     (lineC $ sourceFileBS "input.txt")   -- ConduitT i ByteString m ()  -- by "chunk"
   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Это заставляет меня поверить, что проблема сейчас в том, что первый проходной канал в линии не имеет типа входа, совместимого с runConduitRes.

Я просто не могу понять это, и мне действительно нужен намек.

Заранее большое спасибо.

...