2010-07-25 43 views
3

我試圖嚴格讀取和解碼二進制文件,這似乎在大多數時間都有效。但不幸的是在少數情況下我的計劃失敗,在Haskell中嚴格使用Binary解碼文件的問題

「的字節數太少。無法讀取字節位置1」

我猜二進制其解碼功能認爲沒有數據可用, 但我知道有與只需重新運行該程序就可以正常工作。

我已經嘗試了幾種解決方案,但也不是能用來解決我的問題:(

  • withBinaryFile:

    decodeFile' path = withBinaryFile path ReadMode doDecode 
        where 
        doDecode h = do c <- LBS.hGetContents h 
            return $! decode c 
    
  • 閱讀與它嚴格字節字符串解碼整個文件:

    decodeFile' path = decode . LBS.fromChunks . return <$> BS.readFile path 
    
  • 增加一些更嚴格的要求

    decodeFile' path = fmap (decode . LBS.fromChunks . return) $! BS.readFile path 
    

任何想法是怎麼回事,如何解決這一問題?

謝謝!

編輯:我想我已經找到了我的問題。這不是嚴格閱讀文件。我有一些主要從文件中讀取的進程,但有時需要寫入,這會先截斷文件並添加新內容。因此,爲了編寫,我需要首先設置一個文件鎖,當使用「Binary.encodeFile」(當我說流程我不是線程,但是運行同一程序的實例時)時,似乎沒有這樣做。

編輯終於有一段時間來解決我的問題,使用POSIX IO和文件鎖定。從那以後我就沒有更多的問題了。

爲了防止有人對我當前的解決方案感興趣,或者有人能夠指出錯誤/問題,我會在此處發佈我的解決方案。

安全編碼文件:

safeEncodeFile path value = do 
    fd <- openFd path WriteOnly (Just 0o600) (defaultFileFlags {trunc = True}) 
    waitToSetLock fd (WriteLock, AbsoluteSeek, 0, 0) 
    let cs = encode value 
    let outFn = LBS.foldrChunks (\c rest -> writeChunk fd c >> rest) (return()) cs 
    outFn 
    closeFd fd 
    where 
    writeChunk fd bs = unsafeUseAsCString bs $ \ptr -> 
         fdWriteBuf fd (castPtr ptr) (fromIntegral $ BS.length bs) 

和解碼文件:

safeDecodeFile def path = do 
    e <- doesFileExist path 
    if e 
     then do fd <- openFd path ReadOnly Nothing 
          (defaultFileFlags{nonBlock=True}) 
       waitToSetLock fd (ReadLock, AbsoluteSeek, 0, 0) 
       c <- fdGetContents fd 
       let !v = decode $! c 
       return v 
     else return def 

fdGetContents fd = lazyRead 
    where 
    lazyRead = unsafeInterleaveIO loop 

    loop = do blk <- readBlock fd 
       case blk of 
       Nothing -> return LBS.Empty 
       Just c -> do cs <- lazyRead 
           return (LBS.Chunk c cs) 

readBlock fd = do buf <- mallocBytes 4096 
        readSize <- fdReadBuf fd buf 4096 
        if readSize == 0 
        then do free buf 
          closeFd fd 
          return Nothing 
        else do bs <- unsafePackCStringFinalizer buf 
             (fromIntegral readSize) 
             (free buf) 
          return $ Just bs 

有了合格的進口嚴格懶字節串爲:

import qualified Data.ByteString as BS 
import qualified Data.ByteString.Lazy as LBS 
import qualified Data.ByteString.Lazy.Internal as LBS 
+0

這不是一個嚴格的問題 - 例如,您的第二個解決方案可以保證讀取所有數據。 您的二進制解析器本身可能存在一些問題嗎? – 2010-07-25 17:47:02

+0

考慮使用穀物而不是二進制。 – 2010-07-25 17:59:41

+0

我只是存儲和加載一個Trie for ByteStrings(包bytestring-trie)。 所以我對二進制比如get函數基本上是這樣的: 「GET = DO特里< - 獲得;返回$ DATA(尺寸線索)特里」 這就是爲什麼我使用二進制,因爲字節串,特里包已經有一個二元實例,我不認爲穀物會是一個選擇。 我的程序是從shell運行的,我想在某些情況下,兩個實例可能同時訪問該文件...所以也許這是由於文件鎖定?是否有任何方法來設置和等待文件鎖定被釋放? – urso 2010-07-25 19:21:08

回答

1

這將是,如果有幫助您可以生成一些運行並顯示問題的最小代碼片段。現在我不相信這對您的程序跟蹤來說不是一個問題,哪些句柄是打開/關閉的,以及讀取/寫入妨礙彼此。這裏是我做的很好的示例測試代碼。

import Data.Trie as T 
import qualified Data.ByteString as B 
import qualified Data.ByteString.Lazy as L 
import Data.Binary 
import System.IO 

tmp = "blah" 

main = do 
    let trie = T.fromList [(B.pack [p], p) | p <- [0..]] 
    (file,hdl) <- openTempFile "/tmp" tmp 
    B.hPutStr hdl (B.concat $ L.toChunks $ encode trie) 
    hClose hdl 
    putStrLn file 
    t <- B.readFile file 
    let trie' = decode (L.fromChunks [t]) 
    print (trie' == trie) 
+0

謝謝。它或多或少與您的代碼相似,但讀取和寫入交換。 1.讀取數據,2.修改數據,3.使用Binary.encodeFile寫入數據,這會在寫入之前截斷文件。所以我認爲這是一個讀取文件的過程中負載覆蓋它的競爭條件(見我的文章「編輯」)。 – urso 2010-07-27 15:09:26