Как сохранить, добавить и прочитать список кортежей, включая списки, в файл, используя Data.Serialize и ByteString - PullRequest
0 голосов
/ 16 сентября 2018

Здравствуйте, у меня проблемы с чтением после сохранения и добавления списка списков кортежей в файл.

Сохранение чего-либо в файл работает без проблем.

Я сохраняю в файл с

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 отсутствует что-то вроде карты, но я не могу понять, как.

Я ценю любую помощь или любые другие решения, поэтому использование Data.Serialize и ByteString не является обязательным. Другие возможности, о которых я подумал, - это файлы json с Data.Aeson, если я не могу заставить его работать с Serialize

Редактировать:

Я понял, что допустил ошибку в типе декодирования в fromFile

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

должно быть так

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

1 Ответ

0 голосов
/ 16 сентября 2018

Краткое описание проблемы Формат по умолчанию, используемый сериализованным (или двоичным) кодированием, тривиально не добавляется.

Проблема (длиннее)

Вы говорите, что добавили:

S.encode (a,b)

к одному и тому же файлу "несколько раз". Итак, формат файла теперь:

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

Повторяется, сколько раз вы добавляли файл. То есть каждое добавление будет добавлять новые поля длины и список значений с плавающей точкой, оставляя старые значения на месте.

После этого вы вернулись, чтобы прочитать файл и декодировать некоторые поплавки, используя, морально, S.decode <$> BS.readFile path. Это будет декодировать первые два списка с плавающей точкой, сначала читая поле длины (в первый раз, когда вы записали в файл), затем следующие значения с плавающей точкой и второе поле длины, за которым следуют связанные с ними значения с плавающей точкой. После считывания заданной длины значений float декодер остановится.

Теперь должно быть ясно, что только из-за того, что вы добавили больше данных, ваш скрипт кодирования или декодирования не будет искать никаких дополнительных данных. Формат по умолчанию, используемый сериализованным (или двоичным) кодированием, тривиально не добавляется.

* Решения 1023 *

Вы упомянули переход на 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

Итак, мы написали наш собственный метод получения (непроверенный), который будет проверять, останется ли байт, и если да, то декодировать другую пару списков с плавающей точкой. Каждый раз, когда он декодирует новый чанк, он добавляет старый чанк (который неэффективен, используйте что-то вроде dlist, если вы хотите, чтобы он был респектабельным).

...