mirror of
https://github.com/unknwon/the-way-to-go_ZH_CN.git
synced 2025-11-13 17:36:12 +08:00
@@ -6,9 +6,9 @@
|
||||
|
||||
(本节代码见 [goto_v2/store.go](examples/chapter_19/goto_v2/store.go) 和 [goto_v2/main.go](examples/chapter_19/goto_v2/main.go)。)
|
||||
|
||||
当 goto 进程(监听在 8080 端口的 web 服务器)终止,这迟早会发生,内存 map 中缩短的 URL 就会丢失。要保留这些数据,就得将其保存到磁盘文件中。我们将修改 `URLStore`,使它可以保存数据到文件,且在 goto 启动时还原这些数据。为此我们使用 Go 标准库的 `encoding/gob` 包:它用于序列化和反序列化,将数据结构转换为字节数组(确切地说是切片),反之亦然(见 [12.11 节](12.11.md))。
|
||||
当 goto 进程(监听在 8080 端口的 web 服务器)终止,这迟早会发生,内存 `map` 中缩短的 URL 就会丢失。要保留这些数据,就得将其保存到磁盘文件中。我们将修改 `URLStore()`,使它可以保存数据到文件,且在 goto 启动时还原这些数据。为此我们使用 Go 标准库的 `encoding/gob` 包:它用于序列化和反序列化,将数据结构转换为字节数组(确切地说是切片),反之亦然(见 [12.11 节](12.11.md))。
|
||||
|
||||
通过 `gob` 包的 `NewEncoder` 和 `NewDecoder` 函数,可以指定数据要写入或读取的位置。返回的 `Encoder` 和 `Decoder` 对象提供了 `Encode` 和 `Decode` 方法,用于对文件写入和从中读取 Go 数据结构。提示:`Encoder` 实现了 `Writer` 接口,同样 `Decoder` 实现了 `Reader` 接口。我们在 `URLStore` 上增加一个新的 `file` 字段(`*os.File` 类型),它是用于读写已打开文件的句柄。
|
||||
通过 `gob` 包的 `NewEncoder()` 和 `NewDecoder()` 函数,可以指定数据要写入或读取的位置。返回的 `Encoder` 和 `Decoder` 对象提供了 `Encode` 和 `Decode` 方法,用于对文件写入和从中读取 Go 数据结构。提示:`Encoder` 实现了 `Writer` 接口,同样 `Decoder` 实现了 `Reader` 接口。我们在 `URLStore` 上增加一个新的 `file` 字段(`*os.File` 类型),它是用于读写已打开文件的句柄。
|
||||
|
||||
|
||||
```go
|
||||
@@ -24,7 +24,7 @@ type URLStore struct {
|
||||
var store = NewURLStore("store.gob")
|
||||
```
|
||||
|
||||
接着,调整 `NewURLStore` 函数:
|
||||
接着,调整 `NewURLStore()` 函数:
|
||||
```go
|
||||
func NewURLStore(filename string) *URLStore {
|
||||
s := &URLStore{urls: make(map[string]string)}
|
||||
@@ -37,9 +37,9 @@ func NewURLStore(filename string) *URLStore {
|
||||
}
|
||||
```
|
||||
|
||||
现在,更新后的 `NewURLStore` 函数接受一个文件名参数,它会打开该文件(见 [12 章](12.0.md)),将返回的 `*os.File` 作为 `file` 字段的值存储在 `URLStore` 变量 `store` 中,即这里的本地变量 `s` 。
|
||||
现在,更新后的 `NewURLStore()` 函数接受一个文件名参数,它会打开该文件(见 [12 章](12.0.md)),将返回的 `*os.File` 作为 `file` 字段的值存储在 `URLStore` 变量 `store` 中,即这里的本地变量 `s` 。
|
||||
|
||||
对 `OpenFile` 的调用可能会失败(例如文件可能被删除或改名)。它会返回一个错误 err,注意 Go 是如何处理这种情况的:
|
||||
对 `OpenFile()` 的调用可能会失败(例如文件可能被删除或改名)。它会返回一个错误 `err`,注意 Go 是如何处理这种情况的:
|
||||
```go
|
||||
f, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
@@ -47,7 +47,7 @@ if err != nil {
|
||||
}
|
||||
```
|
||||
|
||||
当 err 不为 `nil`,表示确实发生了错误,那么输出一条消息并停止程序执行。这是处理错误的一种方式,大多数情况下错误应该返回给调用函数,但这种检测错误的模式在 Go 代码中也很普遍。在 `}` 之后可以确定文件被成功打开了。
|
||||
当 `err` 不为 `nil`,表示确实发生了错误,那么输出一条消息并停止程序执行。这是处理错误的一种方式,大多数情况下错误应该返回给调用函数,但这种检测错误的模式在 Go 代码中也很普遍。在 `}` 之后可以确定文件被成功打开了。
|
||||
|
||||
打开该文件时启用了写入标志,更精确地说是“追加模式”。每当一对新的短/长 URL 在程序中创建后,我们通过 `gob` 把它存储到文件 "store.gob" 中。
|
||||
|
||||
@@ -58,7 +58,7 @@ type record struct {
|
||||
}
|
||||
```
|
||||
|
||||
以及新的 `save` 方法,将给定的键和 URL 组成 `record` ,以 `gob` 编码的形式写入磁盘。
|
||||
以及新的 `save()` 方法,将给定的键和 URL 组成 `record` ,以 `gob` 编码的形式写入磁盘。
|
||||
```go
|
||||
func (s *URLStore) save(key, url string) error {
|
||||
e := gob.NewEncoder(s.file)
|
||||
@@ -66,7 +66,7 @@ func (s *URLStore) save(key, url string) error {
|
||||
}
|
||||
```
|
||||
|
||||
goto 程序启动时,磁盘上存储的数据必须读取到 `URLStore` 的 map 中。为此,我们编写 `load` 方法:
|
||||
goto 程序启动时,磁盘上存储的数据必须读取到 `URLStore` 的 `map` 中。为此,我们编写 `load` 方法:
|
||||
```go
|
||||
func (s *URLStore) load() error {
|
||||
if _, err := s.file.Seek(0, 0); err != nil {
|
||||
@@ -87,14 +87,14 @@ func (s *URLStore) load() error {
|
||||
}
|
||||
```
|
||||
|
||||
这个新的 `load` 方法会寻址(`Seek`)到文件的起始位置,读取并解码(`Decode`)每一条记录(`record`),然后用 `Set` 方法将数据存储到 map 中。再次注意无处不在的错误处理模式。文件的解码由一个无限循环完成,只要没有错误就会一直继续:
|
||||
这个新的 `load()` 方法会寻址 (`Seek`) 到文件的起始位置,读取并解码 (`Decode`) 每一条记录 (`record`),然后用 `Set` 方法将数据存储到 `map` 中。再次注意无处不在的错误处理模式。文件的解码由一个无限循环完成,只要没有错误就会一直继续:
|
||||
```go
|
||||
for err == nil {
|
||||
…
|
||||
}
|
||||
```
|
||||
|
||||
如果得到了一个错误,可能是刚解码了最后一条记录,于是产生了 `io.EOF`(EndOfFile) 错误。若并非此种错误,表示产生了解码错误,用 `return err` 来返回它。对该方法的调用必须加入到 `NewURLStore` 中:
|
||||
如果得到了一个错误,可能是刚解码了最后一条记录,于是产生了 `io.EOF` (EndOfFile) 错误。若并非此种错误,表示产生了解码错误,用 `return err` 来返回它。对该方法的调用必须加入到 `NewURLStore()` 中:
|
||||
```go
|
||||
func NewURLStore(filename string) *URLStore {
|
||||
s := &URLStore{urls: make(map[string]string)}
|
||||
@@ -110,7 +110,7 @@ func NewURLStore(filename string) *URLStore {
|
||||
}
|
||||
```
|
||||
|
||||
同时在 `Put` 方法中,当新的 URL 对加入到 map 中,也应该立即将它们保存到数据文件中:
|
||||
同时在 `Put()` 方法中,当新的 URL 对加入到 `map` 中,也应该立即将它们保存到数据文件中:
|
||||
```go
|
||||
func (s *URLStore) Put(url string) string {
|
||||
for {
|
||||
@@ -126,7 +126,7 @@ func (s *URLStore) Put(url string) string {
|
||||
}
|
||||
```
|
||||
|
||||
编译并测试这第二个版本的程序,或直接使用现有的可执行程序,验证关闭服务器(在终端窗口可以按 CTRL/C)并重启后,短 URL 仍然有效。goto 程序第一次启动时,文件 store.gob 还不存在,因此当载入数据时会得到错误:
|
||||
编译并测试这第二个版本的程序,或直接使用现有的可执行程序,验证关闭服务器(在终端窗口可以按 CTRL+C)并重启后,短 URL 仍然有效。goto 程序第一次启动时,文件 store.gob 还不存在,因此当载入数据时会得到错误:
|
||||
|
||||
2011/09/11 11:08:11 Error loading URLStore: open store.gob: The system cannot find the file specified.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user