7.3. 数据结构

我们先定义一个结构类型,用于保存数据。wiki系统由一组互联的wiki页面组成,每个wiki页面包含内容和标题。我们定义wiki页面为结构page, 如下:

  type page struct {
      title    string
      body    []byte
  }

类型[]byte表示一个byte slice。(参考Effective Go了解slices的更多信息) 成员body之所以定义为[]byte而不是string类型,是因为[]byte可以直接使用io包的功能。

结构体page描述了一个页面在内存中的存储方式。但是,如果要将数据保存到磁盘的话,还需要给page类型增加save方法:

  func (p *page) save() os.Error {
      filename := p.title + ".txt"
      return ioutil.WriteFile(filename, p.body, 0600)
  }

类型方法的签名可以这样解读:“save为page类型的方法,方法的调用者为page类型的指针变量p。该成员函数没有参数,返回值为os.Error,表示错误信息。”

该方法会将page结构的body部分保存到文本文件中。为了简单,我们用title作为文本文件的名字。

方法save的返回值类型为os.Error,对应WriteFile(标准库函数,将byte slice写到文件中)的返回值。通过返回os.Error值,可以判断发生错误的类型。如果没有错误,那么返回nil(指针、接口和其他一些类型的零值)。

WriteFile的第三个参数为八进制的0600,表示仅当前用户拥有新创建文件的读写权限。(参考Unix手册 open(2) )

下面的函数加载一个页面:

  func loadPage(title string) *page {
      filename := title + ".txt"
      body, _ := ioutil.ReadFile(filename)
      return &page{title: title, body: body}
  }

函数loadPage根据页面标题从对应文件读取页面的内容,并且构造一个新的 page变量——对应一个页面。

go中函数(以及成员方法)可以返回多个值。标准库中的io.ReadFile在返回[]byte的同时还返回os.Error类型的错误信息。前面的代码中我们用下划线“_”丢弃了错误信息。

但是ReadFile可能会发生错误,例如请求的文件不存在。因此,我们给函数的返回值增加一个错误信息。

  func loadPage(title string) (*page, os.Error) {
      filename := title + ".txt"
      body, err := ioutil.ReadFile(filename)
      if err != nil {
          return nil, err
      }
      return &page{title: title, body: body}, nil
  }

现在调用者可以检测第二个返回值,如果为nil就表示成功装载页面。否则,调用者可以得到一个os.Error对象。(关于错误的更多信息可以参考os package documentation)

现在,我们有了一个简单的数据结构,可以保存到文件中,或者从文件加载。我们创建一个main函数,测试相关功能。

  func main() {
      p1 := &page{title: "TestPage", body: []byte("This is a sample page.")}
      p1.save()
      p2, _ := loadPage("TestPage")
      fmt.Println(string(p2.body))
  }

编译后运行以上程序的话,会创建一个TestPage.txt文件,用于保存p1对应的页面内容。然后,从文件读取页面内容到p2,并且将p2的值打印到 屏幕。

可以用类似以下命令编译运行程序:

  $ 8g wiki.go
  $ 8l wiki.8
  $ ./8.out
  This is a sample page.

(命令8g和8l对应GOARCH=386。如果是amd64系统,可以用6g和6l)

点击这里查看我们当前的代码。