2010-07-25 69 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