如何使用Data.Serialize和ByteString将元组列表(包括列表)保存,追加和读取

时间:2018-09-16 14:49:57

标签: haskell serialization bytestring

您好,在保存并在文件内添加元组列表列表后,我遇到了问题。

将某些内容保存到文件中可以正常工作。

我使用

保存到文件中
import qualified Data.ByteString            as BS
import qualified Data.Serialize             as S (decode, encode)
import Data.Either 

toFile path = do
   let a = take 1000 [100..] :: [Float]
   let b = take 100 [1..] :: [Float]
   BS.appendFile path $ S.encode (a,b)

并阅读

fromFile path = do 
    bstr<-BS.readFile path
    let d = S.decode bstr :: Either String ([Float],[Float])
    return (Right d)

但是用fromFile读取该文件只会给我1个元素,尽管我多次附加到该文件。

由于我在文件后追加了多个元素,因此我在fromFile函数上缺少诸如map之类的东西,但是我无法解决。

我感谢任何帮助或任何其他解决方案,因此不必使用Data.Serialize和ByteString。我想到的其他可能性是带有Data.Aeson的json文件,如果我无法使其与Serialize一起使用

编辑:

我意识到我在fromFile的解码类型上犯了一个错误

let d = S.decode bstr :: Either String ([Float],[Float])

应该是这样

let d = S.decode bstr :: Either String [([Float],[Float])]

1 个答案:

答案 0 :(得分:1)

简短的问题序列化(或二进制)编码使用的默认格式不可轻易附加。

问题(更长)

您说您附加了:

S.encode (a,b)

“多次”复制到同一文件。现在文件的格式为:

[ 64 bit length field  | # floats encoded | 64 length field | # floats encoded ]

重复多次,但您多次附加到文件。也就是说,每个附加项都会添加新的长度字段和浮点列表,同时保留旧值。

此后,您返回读取文件并使用S.decode <$> BS.readFile path解码一些浮点数。这将通过先读取(第一次写入文件的)长度字段,然后读取随后的浮点数和第二个长度字段,再读取其相关的浮点数,来解码浮点数的前两个列表。读取了规定长度的浮点数后,解码器将停止。

现在应该清楚,仅仅因为您添加了更多数据并不能使您的编码或解码脚本寻找任何其他数据。序列化(或二进制)编码使用的默认格式不是很容易附加。

解决方案

您提到了切换到Aeson,但是使用JSON而不是二进制进行编码将无济于事。从逻辑上讲,解码两个附加的JSON字符串(例如{ "first": [1], "second": [2]}{ "first": [3], "second": [4]})与您当前的问题相同。您有一些未知数量的交错列表块-只需编写解码器即可继续尝试:

import Data.Serialize as S
import Data.Serialize.Get as S
import Data.ByteString as BS

fromFile path = do 
    bstr <- BS.readFile path
    let d = S.runGet getMultiChunks bstr :: Either String ([Float],[Float])
    return (Right d)

getMultiChunks :: Get ([Float],[Float])
getMultiChunks = go ([], [])
   where
  go (l,r) = do
     b <- isEmpty
     if b then pure ([],[])
          else do (lNext, rNext) <- S.get
                  go (l ++ lNext, r ++ rNext) -- inefficient

因此,我们已经编写了自己的getter(未经测试),该getter可以查看是否剩余字节,如果需要,还可以解码另一对浮点列表。每次它解码一个新块时,它都会在旧块之前添加一个小块(效率低下,如果您希望它受人尊敬,请使用dlist之类的东西)。