Contents

golang-学习笔记-读写数据

go官方文档 go语言参考

在写过很多go代码之后,感觉自己并没有完全掌握go语言,还有很多知识盲区,所以有了这个go学习笔记系列,本系列是作者跟着电子书重新复习go语言相关内容的笔记

读取用户的输入

从键盘和标准输入 os.Stdin 读取输入

fmt 包提供的 Scan 和 Sscan 开头的函数封装了读取过程

也可以使用 bufio 包提供的缓冲读取(buffered reader)来读取数据

1
2
3
4
5
inputReader = bufio.NewReader(os.Stdin)
input, err = inputReader.ReadString('\n')
if err == nil {
    fmt.Printf("The input was: %s\n", input)
}

Unix和Windows的行结束符是不同的

文件读写

读文件

文件使用指向 os.File 类型的指针来表示的,也叫做文件句柄。标准输入 os.Stdin 和标准输出 os.Stdout,他们的类型都是 *os.File。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
inputFile, inputError := os.Open("input.dat")
if inputError != nil {
  fmt.Printf("An error occurred on opening the inputfile\n" +
    "Does the file exist?\n" +
    "Have you got acces to it?\n")
  return // exit the function on error
}
defer inputFile.Close()

inputReader := bufio.NewReader(inputFile)
for {
  inputString, readerError := inputReader.ReadString('\n')
fmt.Printf("The input was: %s", inputString)
  if readerError == io.EOF {
    return
  }      
}

其他类似函数:

  1. 将整个文件的内容读到一个字符串里:ioutil.ReadFile()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
inputFile := "products.txt"
outputFile := "products_copy.txt"
buf, err := ioutil.ReadFile(inputFile)
if err != nil {
  fmt.Fprintf(os.Stderr, "File Error: %s\n", err)
  // panic(err.Error())
  }
fmt.Printf("%s\n", string(buf))
err = ioutil.WriteFile(outputFile, buf, 0644) // oct, not hex
if err != nil {
  panic(err.Error())
}
  1. 带缓冲的读取:二进制文件使用bufio.Reader 的 Read()
1
2
3
4
buf := make([]byte, 1024)
...
n, err := inputReader.Read(buf)
if (n == 0) { break}
  1. 按列读取文件中的数据:FScan
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
file, err := os.Open("products2.txt")
if err != nil {
  panic(err)
}
defer file.Close()

var col1, col2, col3 []string
for {
  var v1, v2, v3 string
  _, err := fmt.Fscanln(file, &v1, &v2, &v3)
  // scans until newline
  if err != nil {
      break
  }
  col1 = append(col1, v1)
  col2 = append(col2, v2)
  col3 = append(col3, v3)
}

path 包里包含一个子包叫 filepath,这个子包提供了跨平台的函数,用于处理文件名和路径。

1
2
import "path/filepath"
filename := filepath.Base(path)

compress包:读取压缩文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fName := "MyFile.gz"
var r *bufio.Reader
fi, err := os.Open(fName)
if err != nil {
  fmt.Fprintf(os.Stderr, "%v, Can't open %s: error: %s\n", os.Args[0], fName,
      err)
  os.Exit(1)
}
fz, err := gzip.NewReader(fi)
if err != nil {
  r = bufio.NewReader(fi)
} else {
  r = bufio.NewReader(fz)
}

for {
  line, err := r.ReadString('\n')
  if err != nil {
      fmt.Println("Done reading file")
      os.Exit(0)
  }
  fmt.Println(line)
}

相当于对io.Reader进行了一层封装,得到gzip.Reader,之后再用bufio读取。

写文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
outputFile, outputError := os.OpenFile("output.dat", os.O_WRONLY|os.O_CREATE, 0666)
if outputError != nil {
  fmt.Printf("An error occurred with file opening or creation\n")
  return  
}
defer outputFile.Close()

outputWriter := bufio.NewWriter(outputFile)
outputString := "hello world!\n"

for i:=0; i<10; i++ {
  outputWriter.WriteString(outputString)
}
outputWriter.Flush()

os.Open()函数内部仍然是调用OpenFile()函数,以只读打开文件。

OpenFile 函数有三个参数:文件名、一个或多个标志(使用逻辑运算符“|”连接),使用的文件权限。常用标志:

  • os.O_RDONLY:只读
  • os.O_WRONLY:只写
  • os.O_CREATE:创建:如果指定文件不存在,就创建该文件。
  • os.O_TRUNC:截断:如果指定文件已存在,就将该文件的长度截为0。

创建一个写入器(缓冲区)对象:outputWriter := bufio.NewWriter(outputFile)

使用一个 for 循环,将字符串写入缓冲区

outputWriter.Flush()将缓存区的内容显示传输过去

文件拷贝

io.Copy(dst, src)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
src, err := os.Open(srcName)
if err != nil {
  return
}
defer src.Close()

dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
  return
}
defer dst.Close()

io.Copy(dst, src)

从命令行读取参数

os 包

os 包中有一个 string 类型的切片变量 os.Args,用来处理一些基本的命令行参数,它在程序启动后读取命令行输入的参数。os.Args[0] 放的是程序本身的名字。

flag 包

用 buffer 读取文件

bufio.NewReader(os.Stdin)

用切片读写文件

1
2
3
4
5
var buf [NBUF]byte

if nw, ew := os.Stdout.Write(buf[0:nr]); nw != nr {
  fmt.Fprintf(os.Stderr, "cat: error writing: %s\n", ew.Error())
}

用 defer 关闭文件

1
2
f, _ := os.OpenFile(name, os.O_RDONLY, 0)
defer f.Close()

使用接口的实际例子:fmt.Fprintf

1
2
3
4
5
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)

type Writer interface {
    Write(p []byte) (n int, err error)
}

JSON 数据格式

一些术语说明:

  • 数据结构 –> 指定格式 = 序列化 或 编码(传输之前)
  • 指定格式 –> 数据格式 = 反序列化 或 解码(传输之后)
1
2
3
func Marshal(v interface{}) ([]byte, error)

func Unmarshal(data []byte, v interface{}) error

解码任意的数据:

json 包使用 map[string]interface{} 和 []interface{} 储存任意的 JSON 对象和数组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
b := []byte(`{"Name": "Wednesday", "Age": 6, "Parents": ["Gomez", "Morticia"]}`)

var f interface{}
err := json.Unmarshal(b, &f)

//f 指向的值是一个 map,key 是一个字符串,value 是自身存储作为空接口类型的值:
// map[string]interface{} {
//     "Name": "Wednesday",
//     "Age":  6,
//     "Parents": []interface{} {
//         "Gomez",
//         "Morticia",
//     },
// }

//要访问这个数据,我们可以使用类型断言
m := f.(map[string]interface{})

//通过 for range 语法和 type switch 来访问其实际类型:
for k, v := range m {
  switch vv := v.(type) {
  case string:
    fmt.Println(k, "is string", vv)
  case int:
    fmt.Println(k, "is int", vv)

  case []interface{}:
    fmt.Println(k, "is an array:")
    for i, u := range vv {
        fmt.Println(i, u)
    }
  default:
    fmt.Println(k, "is of a type I don’t know how to handle")
  }
}

解码数据到结构

1
2
3
4
5
6
7
8
type FamilyMember struct {
    Name    string
    Age     int
    Parents []string
}

var m FamilyMember
err := json.Unmarshal(b, &m)

编码和解码流:把 JSON 直接写入文件

1
2
3
4
5
func NewDecoder(r io.Reader) *Decoder
func NewEncoder(w io.Writer) *Encoder

func NewDecoder(r io.Reader) *Decoder
func (dec *Decoder) Decode(v interface{}) error

用 Gob 传输数据

和 JSON 的使用方式一样

Go 中的密码学

  • hash 包:实现了 adler32、crc32、crc64 和 fnv 校验;
  • crypto 包:实现了其它的 hash 算法,比如 md4、md5、sha1 等。以及完整地实现了 aes、blowfish、rc4、rsa、xtea 等加密算法。
 |