簡體   English   中英

在Parsec中流式傳輸讀寫

[英]Streaming reads and writes in Parsec

給定parsec庫中的Parser ,什么是進行流讀取(從輸入文件)和寫入(將已解析的blob /行追加到輸出文件)的好方法。 以下是Text.Parsec.ByteString的示例:

main    = do{ result <- parseFromFile numbers "digits.txt"
             ; case result of
                 Left err  -> print err
                 Right xs  -> print (sum xs)
             }

上面的示例僅從“ digits.txt”中讀取數據,直到看到所有輸入后才顯示輸出。 讓我們假設,不是要收集所有值並進行上述的歸約( sum ),而是要以流方式將所有xs寫入“ digitsOut.txt”(從digits.txt中讀取行並將其寫入digitsOut.txt) 。 給定parseFromFile簽名,在我們看到所有輸入之前,我們似乎不能懶惰地進行流傳輸並將其輸出到輸出文件。 這是類型簽名:

parseFromFile :: Parser a -> String -> IO (Either ParseError a)

因此,為了確定是否有錯誤,似乎需要整個輸入。 如果我沒記錯的話,代碼必須先查看所有輸入,然后才能編寫輸出。 還有另一種使用Parsec進行流輸入和輸出的方法(如果可以避免的話,則沒有AttoParsec ,我想訪問Parsec錯誤報告)。 我要解析的文件很大(50 + GB)。 因此,我需要將解析器代碼與流輸入和輸出連接起來。 如果某個地方有一個很好的例子,指針將不勝感激。

更新資料

AttoParsec 文檔中發現, Parsec無法增量使用輸入。 因此,無法在Parsec進行流式傳輸。 我現在將解析器重構為AttoParsec

Parsec通常專注於少量復雜數據。 考慮編程和標記語言,困難的二進制格式等。 這有兩個效果:

  1. 它針對性能方面的錯誤消息(需要復雜的數據)進行了優化(少量數據不需要)
  2. 它不執行流解析(再次,不需要少量數據)

Attoparsec采用相反的方法:它專注於大量(相對)簡單的數據。 考慮挖掘日志文件,讀取測量值,從Internet等收集數據流。 這有兩個效果:

  1. 它針對錯誤消息的性能進行了優化
  2. 它圍繞流媒體構建

使用Attoparsec進行流式解析的背后原理是,您選擇解析某些內容,當它消耗完所有輸入后,它將返回一個值,該值指示期望輸入更多的輸入。

請注意,但是,即使使用Attoparsec對其進行解析,一個巨大的文件仍可能會占用大量內存。 這是因為Attoparsec(與Parsec相對)在無法與解析器匹配時總是回溯,因此它不能丟棄“已經解析”的數據,因為以后可能需要它。

如注釋中所述,解決方案是只為數據的單個部分編寫一個解析器(類似於日志文件的一行),然后從Haskell代碼中為日志文件的每一行重復運行該解析器。 。 這允許解析占用恆定的空間。

給定parseFromFile簽名,在我們看到所有輸入之前,我們似乎不能懶惰地進行流傳輸並將其輸出到輸出文件。 [...]因此,為了確定是否存在錯誤,似乎需要整個輸入。 如果我沒記錯的話,代碼必須先查看所有輸入,然后才能編寫輸出。

Attoparsec和Parsec都一樣。 盡管Attoparsec使您可以更好地控制輸入的增量消耗,但仍無法使解析器表達輸出的增量產量。 解析器必須先完成操作才能報告成功,並且獲取解析器輸出的唯一方法是通過表示解析器成功的數據構造函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM