2012-01-24 38 views
5

我有一個簡單的基於attoparsec的pdf parser。它工作正常,直到與iteratee一起使用。 輸入大小超過緩衝區大小時。當輸入大於緩衝區大小時,attoparsec-iteratee不起作用

import qualified Data.ByteString as BS 
import qualified Data.Iteratee as I 
import qualified Data.Attoparsec as P 
import qualified Data.Attoparsec.Iteratee as P 
import System.Environment (getArgs) 
import Control.Monad 

import Pdf.Parser.Value 

main :: IO() 
main = do 
    [i] <- getArgs 
    liftM (P.parseOnly parseValue) (BS.readFile i) >>= print -- works 
    I.fileDriverRandomVBuf 2048 (P.parserToIteratee parseValue) i >>= print -- works 
    I.fileDriverRandomVBuf 1024 (P.parserToIteratee parseValue) i >>= print -- DOES NOT works!!! 

輸入:

<< /Annots [ 404 0 R 547 0 R ] /ArtBox [ 0.000000 0.000000 612.000000 792.000000 ] /BleedBox [ 0.000000 0.000000 612.000000 792.000000 ] /Contents [ 435 0 R 436 0 R 437 0 R 444 0 R 448 0 R 449 0 R 450 0 R 453 0 R ] /CropBox [ 0.000000 0.000000 612.000000 792.000000 ] /Group 544 0 R /MediaBox [ 0.000000 0.000000 612.000000 792.000000 ] /Parent 239 0 R /Resources << /ColorSpace << /CS0 427 0 R /CS1 427 0 R /CS2 428 0 R >> /ExtGState << /GS0 430 0 R /GS1 431 0 R /GS2 469 0 R /GS3 475 0 R /GS4 439 0 R /GS5 480 0 R /GS6 485 0 R /GS7 491 0 R /GS8 497 0 R >> /Font << /C2_0 447 0 R /T1_0 421 0 R /T1_1 422 0 R /T1_2 423 0 R /T1_3 424 0 R /T1_4 425 0 R /T1_5 426 0 R /T1_6 438 0 R >> /ProcSet [ /PDF /Text /ImageC /ImageI ] /Properties << /MC0 << /Metadata 502 0 R >> >> /XObject << /Fm0 451 0 R /Fm1 504 0 R /Fm2 513 0 R /Fm3 515 0 R /Fm4 517 0 R /Fm5 526 0 R /Fm6 528 0 R /Fm7 537 0 R /Fm8 539 0 R /Im0 540 0 R /Im1 541 0 R /Im2 452 0 R /Im3 542 0 R /Im4 543 0 R >> >> /Rotate 0 /StructParents 1 /TrimBox [ 0.000000 0.000000 612.000000 792.000000 ] /Type /Page >> 

因此,分析器工作沒有iteratee,有足夠大大塊的作品,但不與較小的塊工作。迭代中的錯誤?在attoparsec-iteratee?在我的代碼?有什麼解決方法嗎?這對我來說是一個非常緊迫的問題。

謝謝。

+0

不知道錯誤在哪裏,但可以使用足夠大的塊大小嗎?或者使用'ByteString's而不是'Iteratees'? –

+0

pdf值可以是任意長的,所以沒有足夠大的塊大小。 Re ByteString:你的意思是懶惰的IO? Pdf需要隨機訪問,而參考表通常位於文件的末尾。所以懶惰IO〜=「嚴格」在這種特殊情況下,並且會低效地使用內存。 – Yuras

+0

'Iteratee'允許隨機訪問嗎?我沒有聽說過(沒有任何意義,我不是用戶)。如果您需要隨機訪問,請一次讀取整個文件或使用一些腳手架來查找和讀取文件的某些部分。如果可能的話,第一個選項是**更簡單。 –

回答

2

編輯2:我在PDF /分析器/值

dictOrStream :: Parser PdfValue 
dictOrStream = do 
    dict <- parseDict 
    P.skipSpace 
    let s1 = do 
      P.string $ fromString "stream" 
      content <- P.manyTill P.anyWord8 $ P.endOfLine >> P.string (fromString "endstream") 
      return $ PdfValStream (PdfStream dict (BS.pack content)) 
    s1 <|> return (PdfValDict dict) 

然後在parseValue使用該解析器創建一個新的解析器。這適用於你所有的情況。我不知道爲什麼choice無法正確回溯,也許是一個attoparsec錯誤?

編輯:我注意到,如果我用parseDict代替頂層parseValue,它就可以工作。如果我從parseValue的選項中刪除parseStream,它也可以。我認爲attoparsec在完成頂級字典之後已經承諾「parseStream」,因此它期望更多的輸入(空間,「流」標記等)導致這個錯誤。此時,您需要解決這兩個解析選項之間的不明確性。我不知道爲什麼它可以在整個輸入可用時正常工作;我希望當你的解析器被分塊時,會報告一個錯誤。

截至目前,我懷疑你的代碼或可能attoparsec的錯誤。我通過手動讀取字節串塊,並將其輸送到你的attoparsec分析器運行以下測試:

*Main System.IO> h <- openFile "test.pdf" ReadMode 
*Main System.IO Data.ByteString> let hget = hGetSome h 1024 
*Main System.IO Data.ByteString> b <- hget 
*Main System.IO Data.ByteString> let r = P.parse parseValue b 
*Main System.IO Data.ByteString> r 
Partial _ 
*Main System.IO Data.ByteString> b <- hget 
*Main System.IO Data.ByteString> let r' = P.feed r b 
*Main System.IO Data.ByteString> r' 
Partial _ 
*Main System.IO Data.ByteString> b <- hget 
*Main System.IO Data.ByteString> Data.ByteString.length b 
0 
*Main System.IO Data.ByteString> let r'2 = P.feed r' b 
*Main System.IO Data.ByteString> r'2 
Fail "<< /Annots [ 404 0 R 547 0 R ] /ArtBox [ 0.000000 0.000000 612.000000 792.000000 ] /BleedBox [ 0.000000 0.000000 612.000000 792.000000 ] /Contents [ 435 0 R 436 0 R 437 0 R 444 0 R 448 0 R 449 0 R 450 0 R 453 0 R ] /CropBox [ 0.000000 0.000000 612.000000 792.000000 ] /Group 544 0 R /MediaBox [ 0.000000 0.000000 612.000000 792.000000 ] /Parent 239 0 R /Resources << /ColorSpace << /CS0 427 0 R /CS1 427 0 R /CS2 428 0 R >> /ExtGState << /GS0 430 0 R /GS1 431 0 R /GS2 469 0 R /GS3 475 0 R /GS4 439 0 R /GS5 480 0 R /GS6 485 0 R /GS7 491 0 R /GS8 497 0 R >> /Font << /C2_0 447 0 R /T1_0 421 0 R /T1_1 422 0 R /T1_2 423 0 R /T1_3 424 0 R /T1_4 425 0 R /T1_5 426 0 R /T1_6 438 0 R >> /ProcSet [ /PDF /Text /ImageC /ImageI ] /Properties << /MC0 << /Metadata 502 0 R >> >> /XObject << /Fm0 451 0 R /Fm1 504 0 R /Fm2 513 0 R /Fm3 515 0 R /Fm4 517 0 R /Fm5 526 0 R /Fm6 528 0 R /Fm7 537 0 R /Fm8 539 0 R /Im0 540 0 R /Im1 541 0 R /Im2 452 0 R /Im3 542 0 R /Im4 543 0 R >> >> /Rotate 0 /StructParents 1 /TrimBox [ 0.000000 0.000000" [] "Failed reading: empty" 

出於某種原因,你的解析器似乎並不喜歡接收塊數據,並且在接收到第三失敗(空)塊而不消耗任何輸入。我還沒有弄清楚你的解析器出錯了,但它絕對不是迭代器或attoparsec-iteratee。

+0

你是對的,看起來像iteratee和attoparsec-iteratee都沒有關係。 ty,John – Yuras

+0

你能解釋一下爲什麼它含糊不清?我希望'parserDict'會失敗,如果沒有找到「stream」,並且'choice'會嘗試下一個選項 - 'parseDict'。 – Yuras

+0

對不起,我的意思是'parseStream'將失敗,如果沒有找到「流」 – Yuras