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
}
}
|
其他类似函数:
- 将整个文件的内容读到一个字符串里:
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())
}
|
- 带缓冲的读取:二进制文件使用bufio.Reader 的 Read()
1
2
3
4
|
buf := make([]byte, 1024)
...
n, err := inputReader.Read(buf)
if (n == 0) { break}
|
- 按列读取文件中的数据: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 等加密算法。