Проблемы декодирования файла строго с бинарного в Haskell - PullRequest
3 голосов
/ 25 июля 2010

Я пытаюсь строго прочитать и декодировать двоичный файл, который, кажется, работает большую часть времени. Но, к сожалению, в некоторых случаях моя Программа не работает с

"слишком мало байтов. Ошибка чтения в позиции байта 1"

Я полагаю, что двоичная функция декодирования считает, что нет доступных данных, но я знаю, что есть и просто перезапуск программы работает нормально.

Я пробовал несколько решений, но ни одно из них не смогло решить мою проблему: (

  • с использованием withBinaryFile:

    decodeFile' path = withBinaryFile path ReadMode doDecode
      where
        doDecode h = do c <- LBS.hGetContents h
                        return $! decode c
    
  • чтение всего файла со строгой ByteString и декодирование из него:

    decodeFile' path = decode . LBS.fromChunks . return <$> BS.readFile path
    
  • добавив немного строгости

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

Есть идеи, что здесь происходит и как решить проблему?

Спасибо!

РЕДАКТИРОВАТЬ: Я думаю, что я понял свою проблему. Речь идет не о строгом чтении файла. У меня есть ряд процессов, в основном читающих из файла, но время от времени нужно писать в него, который сначала усекает файл, а затем добавляет новое содержимое. Поэтому для записи мне нужно сначала установить блокировку файла, что, кажется, не делается при использовании «Binary.encodeFile» (когда я говорю «процесс», я имею в виду не потоки, а реальные экземпляры той же самой программы, которая выполняется). 1032 *

РЕДАКТИРОВАТЬ Наконец-то было время решить мою проблему с помощью POSIX IO и File Locks. У меня больше не было проблем с тех пор.

На всякий случай, если кто-то заинтересован в моем текущем решении или, возможно, кто-то сможет указать на ошибки / проблемы, я опубликую свое решение здесь.

Безопасное кодирование в файл:

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

1 Ответ

1 голос
/ 25 июля 2010

Было бы полезно, если бы вы могли создать минимальный фрагмент кода, который запускается и демонстрирует проблему.Сейчас я не уверен, что это не проблема отслеживания вашей программы, какие ручки открываются / закрываются, а чтение / запись мешают друг другу.Вот пример кода теста, который я сделал, который отлично работает.

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)
...