我使用Gob序列化将Message (下面定义的结构)存储在文件中。
type Message struct {
Message string `json:"message"`
From string `json:"from"`
}我成功地将我的Message放在我使用gob序列化的一个片中,然后将这个序列化的片存储在一个文件中。
但是,通过这样做,我需要从文件中加载整个序列化的切片,对其进行解码,追加新的Message,对切片进行编码,并再次将其保存在文件中。这似乎很复杂,对我来说也不是很好的优化。
函数,用于编解码和写/读。
func (m Message) Encode() ([]byte, error) {
var res bytes.Buffer
encoder := gob.NewEncoder(&res)
err := encoder.Encode(m)
if err != nil {
return []byte{}, err
}
return res.Bytes(), nil
}
func (m Message) Write(path string) error {
messages, err := Read(path)
if err != nil {
return err
}
messages = append(messages, m)
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return err
}
defer f.Close()
encoder := gob.NewEncoder(f)
encoder.Encode(messages)
return nil
}
func Read(path string) ([]Message, error) {
f, err := os.OpenFile(path, os.O_RDWR, 0644)
if err != nil {
return []Message{}, err
}
defer f.Close()
m := []Message{}
decoder := gob.NewDecoder(f)
if err = decoder.Decode(&m); err != nil {
if err == io.EOF {
return []Message{}, nil
}
return []Message{}, err
}
return m, nil
}一个解决方案是将序列化的Message直接存储在文件中,然后简单地在文件末尾添加新的Message。
我是通过使用os.O_APPEND来追加而不是覆盖entiere文件来实现的:
f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)我还做了其他一些基本的更改,比如用消息替换[]消息,等等。
现在,我可以将Message存储在文件中,只需在文件末尾添加新消息,而无需每次重写entiere文件。
但我必须知道如何读取存储在文件中的Message。
前面的代码只读取第一条消息,忽略文件的其余部分。
我找到了许多逐行读取文件的解决方案,但似乎没有一种解决方案适用于gob序列化对象。
是否可以逐行读取存储gob序列化对象的文件?还是必须继续使用当前的解决方案,即存储序列化的片?
注意:我发现了这个主题(Retrieving gobs written to file by appending several times),它看起来描述的是相同类型的问题,但它几乎是7年前的,+描述了一些更复杂的问题
发布于 2022-10-19 19:57:38
我把这作为一个“答案”发布,但是我的问题还没有解决,如果你认为这更合适的话,我可以把它移到我的初始帖子中。
我从这里找到的代码(Retrieving gobs written to file by appending several times)开始进行了一些测试,并试图考虑答案。
我编写了两个新函数,用于在文件中写入gob,并从文件中读取给定数量的gob:
func write(enc *gob.Encoder, m Message) {
err := enc.Encode(m)
if err != nil {
panic(err)
}
}
func read(filename string, to_load int) {
f, err := os.OpenFile(filename, os.O_RDWR, 0644)
defer f.Close()
if err != nil {
panic(err)
}
dec := gob.NewDecoder(f)
for i := 0; i < to_load; i++ {
var m Message
err = dec.Decode(&m)
if err != nil {
panic(err)
}
fmt.Println("loaded struct:", m)
}
}我从答案(https://stackoverflow.com/a/36386843/17070383)中“理解”的一点是,如果所有这些gob都是用gob.Encoder的不同实例编写的,那么从同一个文件读取多个gob可能会很复杂。
所以我编写了一个函数来生成和返回一个gob.Encoder
func getEncoder(fileName string) (*gob.Encoder, *os.File) {
file, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
panic(err)
}
return gob.NewEncoder(file), file
}然后:
func main() {
enc, f := getEncoder("test.bin")
defer f.Close()
m1 := Message{"Bob", "Hello"}
m2 := Message{"Bob2", "Hello2"}
m3 := Message{"Bob3", "Hello3"}
write(enc, m1)
write(enc, m2)
write(enc, m3)
fmt.Println("Read 2 Message from file : ")
read("test.bin", 2)
m4 := Message{"Bob4", "Hello4"}
write(enc, m4)
fmt.Println()
fmt.Println("Read 4 Message from file : ")
read("test.bin", 4)
}产出:
Read 2 Message from file :
loaded struct: {Bob Hello}
loaded struct: {Bob2 Hello2}
Read 4 Message from file :
loaded struct: {Bob Hello}
loaded struct: {Bob2 Hello2}
loaded struct: {Bob3 Hello3}
loaded struct: {Bob4 Hello4}看起来效果很好。
现在,如果我启动代码,我尝试读取8 gob而不是4 (4由第一次代码执行编写,4由第二次执行编写)
read("test.bin", 8)我有这样的输出:
Read 2 Message from file :
loaded struct: {Bob Hello}
loaded struct: {Bob2 Hello2}
Read 4 Message from file :
loaded struct: {Bob Hello}
loaded struct: {Bob2 Hello2}
loaded struct: {Bob3 Hello3}
loaded struct: {Bob4 Hello4}
panic: gob: duplicate type received
goroutine 1 [running]:
main.read({0x10df092?, 0xc000012018?}, 0x8)
/{path}/main.go:34 +0x1c8
main.main()
/{path}/main.go:72 +0x2c5
exit status 2它读取第一个执行时编写的前4个gob,然后在读取第5个元素时感到恐慌,第二个元素是用一个新实例gob.Encoder编写的。
为了验证这一点,我修改了代码,用第一个gob.Encoder编写了3个gob.Encoder,用另一个编码器编写了最后一个gob,然后尝试读取4gob:
func main() {
enc, f := getEncoder("test.bin")
defer f.Close()
m1 := Message{"Bob", "Hello"}
m2 := Message{"Bob2", "Hello2"}
m3 := Message{"Bob3", "Hello3"}
write(enc, m1)
write(enc, m2)
write(enc, m3)
fmt.Println("Read 2 Message from file : ")
read("test.bin", 2)
new_enc := gob.NewEncoder(f) // New encoder
m4 := Message{"Bob4", "Hello4"}
write(new_enc, m4)// Write m4 with new encoder
fmt.Println()
fmt.Println("Read 4 Message from file : ")
read("test.bin", 4)
}注意:我重新设置了文件"test.bin“
产出:
Read 2 Message from file :
loaded struct: {Bob Hello}
loaded struct: {Bob2 Hello2}
Read 4 Message from file :
loaded struct: {Bob Hello}
loaded struct: {Bob2 Hello2}
loaded struct: {Bob3 Hello3}
panic: gob: duplicate type received
...正如我们所看到的,第四个(用第二个编码器写的)不能被读取。
结论:
我无法真正解释为什么它是那样工作的,但是它似乎不可能在文件中直接存储(并读取) gob序列化的结构,每次有新的记录要保存时,都会在文件末尾追加新的gob。
我真的是新来的,我很乐意对此有更多的解释
我找到了两块解决方案:
每次应用程序启动时,您都会创建一个新文件,并实例化链接到该文件的新的
中写入gob。
gob.Encoder重写同一文件中的内容。然后每次都有一个新的记录,您可以再次使用这个gob.Encoder将您的gob附加到文件中。这两种解决方案看起来都很“糟糕”,因为这意味着在应用程序的整个执行过程中,必须保持流打开(与文件一起)。我对此并不熟悉,但看来你应该避免.
请用你所有的知识完成我的帖子!
nem0z
https://stackoverflow.com/questions/74120213
复制相似问题