Golang学习笔记五-标准库

Golang标准库os模块-文件目录相关

os标准库实现了平台(操作系统)无关的编程接口。

https://pkg.go.dev/std

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package main

import (
"fmt"
"os"
)

// 创建文件
func createFile() {
f, err := os.Create("test.txt")
if err != nil {
fmt.Printf("err: %v\n", err)
} else {
fmt.Printf("f: %v\n", f)
}
}

// 创建目录
func createDir() {
// 创建单个目录
/* err := os.Mkdir("test", os.ModePerm)
if err != nil {
fmt.Printf("err: %v\n", err)
} */
err := os.MkdirAll("test/a/b", os.ModePerm)
if err != nil {
fmt.Printf("err: %v\n", err)
}
}

// 删除目录
func removeDir() {
/* err := os.Remove("test.txt")
if err != nil {
fmt.Printf("err: %v\n", err)
} */

err := os.RemoveAll("test")
if err != nil {
fmt.Printf("err: %v\n", err)
}
}

// 获得工作目录
func getWd() {
dir, err := os.Getwd()
if err != nil {
fmt.Printf("err: %v\n", err)
} else {
fmt.Printf("dir: %v\n", dir)
}
}

// 修改工作目录
func chWd() {
err := os.Chdir("d:/")
if err != nil {
fmt.Printf("err: %v\n", err)
}
fmt.Println(os.Getwd())
}

// 获得临时目录
func getTemp() {
s := os.TempDir()
fmt.Printf("s: %v\n", s)
}

// 重命名文件
func renameFile() {
err := os.Rename("test.txt", "test2.txt")
if err != nil {
fmt.Printf("err: %v\n", err)
}
}

// 读文件
func readFile() {
b, err := os.ReadFile("test2.txt")
if err != nil {
fmt.Printf("err: %v\n", err)
} else {
fmt.Printf("b: %v\n", string(b[:]))
}
}

// 写文件
func writeFile() {
s := "hello world"
os.WriteFile("test2.txt", []byte(s), os.ModePerm)
}

func main() {
// createFile()
// createDir()
// removeDir()
// removeDir()
// getWd()
// chWd()
// renameFile()
// readFile()
// writeFile()
// getTemp()
}

Golang标准库os模块-File文件读操作

这里结束和 File结构体相关的文件读操作

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package main

import (
"fmt"
"os"
)

// 打开关闭文件
func openCloseFile() {
// 只能读
f, _ := os.Open("a.txt")
fmt.Printf("f.Name(): %v\n", f.Name())
// 根据第二个参数 可以读写或者创建
f2, _ := os.OpenFile("a1.txt", os.O_RDWR|os.O_CREATE, 0755)
fmt.Printf("f2.Name(): %v\n", f2.Name())

err := f.Close()
fmt.Printf("err: %v\n", err)
err2 := f2.Close()
fmt.Printf("err2: %v\n", err2)
}

// 创建文件
func createFile() {
// 等价于:OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
f, _ := os.Create("a2.txt")
fmt.Printf("f.Name(): %v\n", f.Name())
// 第一个参数 目录默认:Temp 第二个参数 文件名前缀
f2, _ := os.CreateTemp("", "temp")
fmt.Printf("f2.Name(): %v\n", f2.Name())
}

// 读操作
func readOps() {
// 循环读取
/* f, _ := os.Open("a.txt")
for {
buf := make([]byte, 6)
n, err := f.Read(buf)
fmt.Println(string(buf))
fmt.Printf("n: %v\n", n)
if err == io.EOF {
break
}
}
f.Close()
*/
/* buf := make([]byte, 10)
f2, _ := os.Open("a.txt")
// 从5开始读10个字节
n, _ := f2.ReadAt(buf, 5)
fmt.Printf("n: %v\n", n)
fmt.Printf("string(buf): %v\n", string(buf))
f2.Close() */

// 测试 a目录下面有b和c目录
/* f, _ := os.Open("a")
de, _ := f.ReadDir(-1)
for _, v := range de {
fmt.Printf("v.IsDir(): %v\n", v.IsDir())
fmt.Printf("v.Name(): %v\n", v.Name())
} */

// 定位
f, _ := os.Open("a.txt")
f.Seek(3, 0)
buf := make([]byte, 10)
n, _ := f.Read(buf)
fmt.Printf("n: %v\n", n)
fmt.Printf("string(buf): %v\n", string(buf))
f.Close()

}

func main() {
// openCloseFile()
// createFile()
readOps()
}

Golang标准库os模块-File文件写操作

这里结束和 File结构体相关的文件写操作

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package main

import (
"fmt"
"os"
)

// 打开关闭文件
func openCloseFile() {
// 只能读
f, _ := os.Open("a.txt")
fmt.Printf("f.Name(): %v\n", f.Name())
// 根据第二个参数 可以读写或者创建
f2, _ := os.OpenFile("a1.txt", os.O_RDWR|os.O_CREATE, 0755)
fmt.Printf("f2.Name(): %v\n", f2.Name())

err := f.Close()
fmt.Printf("err: %v\n", err)
err2 := f2.Close()
fmt.Printf("err2: %v\n", err2)
}

// 创建文件
func createFile() {
// 等价于:OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
f, _ := os.Create("a2.txt")
fmt.Printf("f.Name(): %v\n", f.Name())
// 第一个参数 目录默认:Temp 第二个参数 文件名前缀
f2, _ := os.CreateTemp("", "temp")
fmt.Printf("f2.Name(): %v\n", f2.Name())
}

// 读操作
func readOps() {
// 循环读取
/* f, _ := os.Open("a.txt")
for {
buf := make([]byte, 6)
n, err := f.Read(buf)
fmt.Println(string(buf))
fmt.Printf("n: %v\n", n)
if err == io.EOF {
break
}
}
f.Close()
*/
/* buf := make([]byte, 10)
f2, _ := os.Open("a.txt")
// 从5开始读10个字节
n, _ := f2.ReadAt(buf, 5)
fmt.Printf("n: %v\n", n)
fmt.Printf("string(buf): %v\n", string(buf))
f2.Close() */

// 测试 a目录下面有b和c目录
/* f, _ := os.Open("a")
de, _ := f.ReadDir(-1)
for _, v := range de {
fmt.Printf("v.IsDir(): %v\n", v.IsDir())
fmt.Printf("v.Name(): %v\n", v.Name())
} */

// 定位
f, _ := os.Open("a.txt")
f.Seek(3, 0)
buf := make([]byte, 10)
n, _ := f.Read(buf)
fmt.Printf("n: %v\n", n)
fmt.Printf("string(buf): %v\n", string(buf))
f.Close()

}

func main() {
// openCloseFile()
// createFile()
readOps()
}

Golang标准库os包进程相关操作

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
36
37
38
39
40
41
42
43
44
45
package main

import (
"fmt"
"os"
"time"
)

func main() {
// 获得当前正在运行的进程id
fmt.Printf("os.Getpid(): %v\n", os.Getpid())
// 父id
fmt.Printf("os.Getppid(): %v\n", os.Getppid())

//设置新进程的属性
attr := &os.ProcAttr{
//files指定新进程继承的活动文件对象
//前三个分别为,标准输入、标准输出、标准错误输出
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
//新进程的环境变量
Env: os.Environ(),
}

//开始一个新进程
p, err := os.StartProcess("C:\\Windows\\System32\\notepad.exe", []string{"C:\\Windows\\System32\\notepad.exe", "D:\\a.txt"}, attr)
if err != nil {
fmt.Println(err)
}
fmt.Println(p)
fmt.Println("进程ID:", p.Pid)

//通过进程ID查找进程
p2, _ := os.FindProcess(p.Pid)
fmt.Println(p2)

//等待10秒,执行函数
time.AfterFunc(time.Second*10, func() {
//向p进程发送退出信号
p.Signal(os.Kill)
})

//等待进程p的退出,返回进程状态
ps, _ := p.Wait()
fmt.Println(ps.String())
}

Golang标准库os包和环境相关的方法

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
package main

import (
"fmt"
"os"
)

func main() {
// 获得所有环境变量
s := os.Environ()
fmt.Printf("s: %v\n", s)
// 获得某个环境变量
s2 := os.Getenv("GOPATH")
fmt.Printf("s2: %v\n", s2)
// 设置环境变量
os.Setenv("env1", "env1")
s2 = os.Getenv("aaa")
fmt.Printf("s2: %v\n", s2)
fmt.Println("-----------")

// 查找
s3, b := os.LookupEnv("env1")
fmt.Printf("b: %v\n", b)
fmt.Printf("s3: %v\n", s3)

// 替换
os.Setenv("NAME", "gopher")
os.Setenv("BURROW", "/usr/gopher")

fmt.Println(os.ExpandEnv("$NAME lives in ${BURROW}."))

// 清空环境变量
// os.Clearenv()

}

Golang标准库io包

Go 语言中,为了方便开发者使用,将 IO 操作封装在了如下几个包中:

  • io 为 IO 原语(I/O primitives)提供基本的接口 os File Reader Writer
  • io/ioutil 封装一些实用的 I/O 函数
  • fmt 实现格式化 I/O,类似 C 语言中的 printf 和 scanf format fmt
  • bufio 实现带缓冲I/O

io — 基本的 IO 接口

在 io 包中最重要的是两个接口:Reader 和 Writer 接口。本章所提到的各种 IO 包,都跟这两个接口有关,也就是说,只要实现了这两个接口,它就有了 IO 的功能

Reader 接口

1
2
3
type Reader interface {
Read(p []byte) (n int, err error)
}

Writer 接口

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

那些类型实现了Reader和Writer接口

1
2
3
4
5
6
7
8
9
os.File 同时实现了 io.Reader 和 io.Writer
strings.Reader 实现了 io.Reader
bufio.Reader/Writer 分别实现了 io.Reader 和 io.Writer
bytes.Buffer 同时实现了 io.Reader 和 io.Writer
bytes.Reader 实现了 io.Reader
compress/gzip.Reader/Writer 分别实现了 io.Reader 和 io.Writer
crypto/cipher.StreamReader/StreamWriter 分别实现了 io.Reader 和 io.Writer
crypto/tls.Conn 同时实现了 io.Reader 和 io.Writer
encoding/csv.Reader/Writer 分别实现了 io.Reader 和 io.Writer

Golang标准库ioutil包

封装一些实用的 I/O 函数

名称 作用
ReadAll 读取数据,返回读到的字节 slice
ReadDir 读取一个目录,返回目录入口数组 []os.FileInfo
ReadFile 读一个文件,返回文件内容(字节slice)
WriteFile 根据文件路径,写入字节slice
TempDir 在一个目录中创建指定前缀名的临时目录,返回新临时目录的路径
TempFile 在一个目录中创建指定前缀名的临时文件,返回 os.File

Golang标准库fmt包

fmt包实现了格式化的I/O函数,这点类似C语言中的printf和scanf,但是更加简单. format

Scanning

一组类似的函数通过扫描已格式化的文本来产生值。

  1. Scan、Scanf 和 Scanln 从os.Stdin 中读取;
  2. Fscan、Fscanf 和 Fscanln 从指定的 io.Reader 中读取;
  3. Sscan、Sscanf 和 Sscanln 从实参字符串中读取。
  4. Scanln、Fscanln 和 Sscanln在换行符处停止扫描,且需要条目紧随换行符之后;
  5. Scanf、Fscanf 和 Sscanf需要输入换行符来匹配格式中的换行符;其它函数则将换行符视为空格。
  6. Scanf、Fscanf 和 Sscanf 根据格式字符串解析实参,类似于 Printf。例如,%x会将一个整数扫描为十六进制数,而 %v 则会扫描该值的默认表现格式。

实例

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
package main

import "fmt"

// scan
func test1() {
var age int
fmt.Println("请输入年龄:")
fmt.Scan(&age)
fmt.Printf("age: %v\n", age)
}

// Scanf
func test2() {
var name string
fmt.Println("请输入姓名:")
fmt.Scanf("%s", &name)
fmt.Printf("name: %v\n", name)
}

// Scanln
func test3() {
var age int
fmt.Println("请输入年龄:")
fmt.Scanln(&age)
fmt.Printf("age: %v\n", age)
}

func main() {
// test2()
test3()
}

其他实例参考官网

函数

1
func Errorf(format string, a ...interface{}) error

Errorf 根据于格式说明符进行格式化,并将字符串作为满足 error 的值返回,其返回类型是error.

1
func Fprint(w io.Writer, a ...interface{}) (n int, err error) 

Fprint 使用其操作数的默认格式进行格式化并写入到 w。当两个连续的操作数均不为字符串时,它们之间就会添加空格。它返回写入的字节数以及任何遇到的错误。

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

Fprintf 根据于格式说明符进行格式化并写入到 w。它返回写入的字节数以及任何遇到的写入错误。

1
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) 

Fprintln 使用其操作数的默认格式进行格式化并写入到 w。其操作数之间总是添加空格,且总在最后追加一个换行符。它返回写入的字节数以及任何遇到的错误。

实例

参考官网文档

Golang标准库bufio

bufio

bufio包实现了有缓冲的I/O。它包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象。

常量

1
2
3
const (
defaultBufSize = 4096
)

变量

1
2
3
4
5
6
var (
ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
ErrBufferFull = errors.New("bufio: buffer full")
ErrNegativeCount = errors.New("bufio: negative count")
)
1
2
3
4
5
var (
ErrTooLong = errors.New("bufio.Scanner: token too long")
ErrNegativeAdvance = errors.New("bufio.Scanner: SplitFunc returns negative advance count")
ErrAdvanceTooFar = errors.New("bufio.Scanner: SplitFunc returns advance count beyond input")
)

会被Scanner类型返回的错误。

type Reader

1
2
3
4
5
6
7
8
type Reader struct {
buf []byte
rd io.Reader // reader provided by the client
r, w int // buf read and write positions
err error
lastByte int // last byte read for UnreadByte; -1 means invalid
lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}

Reader实现了给一个io.Reader接口对象附加缓冲。

func NewReader

1
func NewReader(rd io.Reader) *Reader

NewReader创建一个具有默认大小缓冲、从r读取的*Reader。NewReader 相当于 NewReaderSize(rd, 4096)

func NewReaderSize

1
func NewReaderSize(rd io.Reader, size int) *Reader

NewReaderSize创建一个具有最少有size尺寸的缓冲、从r读取的Reader。如果参数r已经是一个具有足够大缓冲的 Reader类型值,会返回r。

func (*Reader)Reset(r io.Reader)

1
func (b *Reader) Reset(r io.Reader)

Reset丢弃缓冲中的数据,清除任何错误,将b重设为其下层从r读取数据。

1
2
3
4
5
6
7
8
9
10
func main() {
s := strings.NewReader("ABCEFG")
str := strings.NewReader("123455")
br := bufio.NewReader(s)
b, _ := br.ReadString('\n')
fmt.Println(b)
br.Reset(str)
b, _ = br.ReadString('\n')
fmt.Println(b)
}

func (*Reader)Read

1
func (b *Reader) Read(p []byte) (n int, err error)

Read读取数据写入p。本方法返回写入p的字节数。本方法一次调用最多会调用下层Reader接口一次Read方法,因此返回值n可能小于len(p)。读取到达结尾时,返回值n将为0而err将为io.EOF。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
s := strings.NewReader("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
br := bufio.NewReader(s)
p := make([]byte, 10)

for {
n, err := br.Read(p)
if err == io.EOF {
break
} else {
fmt.Printf("string(p): %v\n", string(p[0:n]))
}
}
}

func (*Reader)ReadByte

1
func (b *Reader) ReadByte() (c byte, err error)

ReadByte读取并返回一个字节。如果没有可用的数据,会返回错误。

func (*Reader)UnreadByte

1
func (b *Reader) UnreadByte() error

UnreadByte吐出最近一次读取操作读取的最后一个字节。(只能吐出最后一个,多次调用会出问题)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
s := strings.NewReader("ABCDEFG")
br := bufio.NewReader(s)

c, _ := br.ReadByte()
fmt.Printf("%c\n", c)

c, _ = br.ReadByte()
fmt.Printf("%c\n", c)

br.UnreadByte()
c, _ = br.ReadByte()
fmt.Printf("%c\n", c)
}

func (*Reader)ReadRune

1
func (b *Reader) ReadRune() (r rune, size int, err error)

ReadRune读取一个utf-8编码的unicode码值,返回该码值、其编码长度和可能的错误。如果utf-8编码非法,读取位置只移动1字节,返回U+FFFD,返回值size为1而err为nil。如果没有可用的数据,会返回错误。

func (*Reader)UnreadRune

1
func (b *Reader) UnreadRune() error

UnreadRune吐出最近一次ReadRune调用读取的unicode码值。如果最近一次读取不是调用的ReadRune,会返回错误。(从这点看,UnreadRune比UnreadByte严格很多)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
s := strings.NewReader("你好,世界!")
br := bufio.NewReader(s)

c, size, _ := br.ReadRune()
fmt.Printf("%c %v\n", c, size)

c, size, _ = br.ReadRune()
fmt.Printf("%c %v\n", c, size)

br.UnreadRune()
c, size, _ = br.ReadRune()
fmt.Printf("%c %v\n", c, size)
}

func (*Reader)ReadLine

1
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)

ReadLine是一个低水平的行数据读取原语。大多数调用者应使用ReadBytes(‘\n’)或ReadString(‘\n’)代替,或者使用Scanner。

ReadLine尝试返回一行数据,不包括行尾标志的字节。如果行太长超过了缓冲,返回值isPrefix会被设为true,并返回行的前面一部分。该行剩下的部分将在之后的调用中返回。返回值isPrefix会在返回该行最后一个片段时才设为false。返回切片是缓冲的子切片,只在下一次读取操作之前有效。ReadLine要么返回一个非nil的line,要么返回一个非nil的err,两个返回值至少一个非nil。

返回的文本不包含行尾的标志字节(”\r\n”或”\n”)。如果输入流结束时没有行尾标志字节,方法不会出错,也不会指出这一情况。在调用ReadLine之后调用UnreadByte会总是吐出最后一个读取的字节(很可能是该行的行尾标志字节),即使该字节不是ReadLine返回值的一部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
s := strings.NewReader("ABC\nDEF\r\nGHI\r\nGHI")
br := bufio.NewReader(s)

w, isPrefix, _ := br.ReadLine()
fmt.Printf("%q %v\n", w, isPrefix)

w, isPrefix, _ = br.ReadLine()
fmt.Printf("%q %v\n", w, isPrefix)

w, isPrefix, _ = br.ReadLine()
fmt.Printf("%q %v\n", w, isPrefix)

w, isPrefix, _ = br.ReadLine()
fmt.Printf("%q %v\n", w, isPrefix)
}

func (*Reader)ReadSlice

1
func (b *Reader) ReadSlice(delim byte) (line []byte, err error)

ReadSlice读取直到第一次遇到delim字节,返回缓冲里的包含已读取的数据和delim字节的切片。该返回值只在下一次读取操作之前合法。如果ReadSlice放在在读取到delim之前遇到了错误,它会返回在错误之前读取的数据在缓冲中的切片以及该错误(一般是io.EOF)。如果在读取到delim之前缓冲就被写满了,ReadSlice失败并返回ErrBufferFull。因为ReadSlice的返回值会被下一次I/O操作重写,调用者应尽量使用ReadBytes或ReadString替代本法功法。当且仅当ReadBytes方法返回的切片不以delim结尾时,会返回一个非nil的错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
s := strings.NewReader("ABC,DEF,GHI,JKL")
br := bufio.NewReader(s)

w, _ := br.ReadSlice(',')
fmt.Printf("%q\n", w)

w, _ = br.ReadSlice(',')
fmt.Printf("%q\n", w)

w, _ = br.ReadSlice(',')
fmt.Printf("%q\n", w)
}

func (*Reader)ReadBytes

1
func (b *Reader) ReadBytes(delim byte) (line []byte, err error)

ReadBytes读取直到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的切片。如果ReadBytes方法在读取到delim之前遇到了错误,它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。当且仅当ReadBytes方法返回的切片不以delim结尾时,会返回一个非nil的错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
s := strings.NewReader("ABC DEF GHI JKL")
br := bufio.NewReader(s)

w, _ := br.ReadBytes(' ')
fmt.Printf("%q\n", w)

w, _ = br.ReadBytes(' ')
fmt.Printf("%q\n", w)

w, _ = br.ReadBytes(' ')
fmt.Printf("%q\n", w)
}

func (*Reader)ReadString

1
func (b *Reader) ReadString(delim byte) (line string, err error)

ReadString读取直到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的字符串。如果ReadString方法在读取到delim之前遇到了错误,它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。当且仅当ReadString方法返回的切片不以delim结尾时,会返回一个非nil的错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
s := strings.NewReader("ABC DEF GHI JKL")
br := bufio.NewReader(s)

w, _ := br.ReadString(' ')
fmt.Printf("%q\n", w)

w, _ = br.ReadString(' ')
fmt.Printf("%q\n", w)

w, _ = br.ReadString(' ')
fmt.Printf("%q\n", w)
}

func (*Reader)WriteTo

1
func (b *Reader) WriteTo(w io.Writer) (n int64, err error)

WriteTo方法实现了io.WriterTo接口。

1
2
3
4
5
6
7
8
func main() {
s := strings.NewReader("ABCEFGHIJKLMN")
br := bufio.NewReader(s)
b := bytes.NewBuffer(make([]byte, 0))

br.WriteTo(b)
fmt.Printf("%s\n", b)
}

type Writer

1
2
3
4
5
6
type Writer struct {
err error
buf []byte
n int
wr io.Writer
}

Writer实现了为io.Writer接口对象提供缓冲。如果在向一个Writer类型值写入时遇到了错误,该对象将不再接受任何数据,且所有写操作都会返回该错误。在说有数据都写入后,调用者有义务调用Flush方法以保证所有的数据都交给了下层的io.Writer。

func NewWriter

1
func NewWriter(w io.Writer) *Writer

NewWriter创建一个具有默认大小缓冲、写入w的*Writer。NewWriter 相当于 NewWriterSize(wr, 4096)

func NewWriterSize

1
func NewWriterSize(w io.Writer, size int) *Writer

NewWriterSize创建一个具有最少有size尺寸的缓冲、写入w的Writer。如果参数w已经是一个具有足够大缓冲的 Writer类型值,会返回w。

func (*Writer)Reset

1
func (b *Writer) Reset(w io.Writer)

Reset丢弃缓冲中的数据,清除任何错误,将b重设为将其输出写入w。

1
2
3
4
5
6
7
8
9
10
11
func main() {
b := bytes.NewBuffer(make([]byte, 0))
bw := bufio.NewWriter(b)
bw.WriteString("123456789")
c := bytes.NewBuffer(make([]byte, 0))
bw.Reset(c)
bw.WriteString("456")
bw.Flush()
fmt.Println(b)
fmt.Println(c)
}

func (*Writer)Bufferd

1
func (b *Writer) Buffered() int

Buffered返回缓冲中已使用的字节数。

func (*Writer)Available

1
func (b *Writer) Available() int

Available返回缓冲中还有多少字节未使用。

func (*Writer) Write

1
func (b *Writer) Write(p []byte) (nn int, err error)

Write将p的内容写入缓冲。返回写入的字节数。如果返回值nn < len(p),还会返回一个错误说明原因。

func (*Writer) WriteString

1
func (b *Writer) WriteString(s string) (int, error)

WriteString写入一个字符串。返回写入的字节数。如果返回值nn < len(s),还会返回一个错误说明原因。

func (*Writer) WriteByte

1
func (b *Writer) WriteByte(c byte) error

WriteByte写入单个字节。

func (*Writer) WriteRune

1
func (b *Writer) WriteRune(r rune) (size int, err error)

WriteRune写入一个unicode码值(的utf-8编码),返回写入的字节数和可能的错误。

func (*Writer) Flush

1
func (b *Writer) Flush() error

Flush方法将缓冲中的数据写入下层的io.Writer接口。

func (*Writer) ReadFrom

1
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error)

ReadFrom实现了io.ReaderFrom接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
b := bytes.NewBuffer(make([]byte, 0))
bw := bufio.NewWriter(b)
fmt.Println(bw.Available()) // 4096
fmt.Println(bw.Buffered()) // 0

bw.WriteString("ABCDEFGHIJKLMN")
fmt.Println(bw.Available())
fmt.Println(bw.Buffered())
fmt.Printf("%q\n", b)

bw.Flush()
fmt.Println(bw.Available())
fmt.Println(bw.Buffered())
fmt.Printf("%q\n", b)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func main() {
b := bytes.NewBuffer(make([]byte, 0))
bw := bufio.NewWriter(b)
// 写入缓存
// byte等同于 int8
bw.WriteByte('H')
bw.WriteByte('e')
bw.WriteByte('l')
bw.WriteByte('l')
bw.WriteByte('o')
bw.WriteByte(' ')
// rune等同于int32
bw.WriteRune('世')
bw.WriteRune('界')
bw.WriteRune('!')
// 写入b
bw.Flush()
fmt.Println(b)
}
1
2
3
4
5
6
7
8
func main() {
b := bytes.NewBuffer(make([]byte, 0))
s := strings.NewReader("Hello 世界!")
bw := bufio.NewWriter(b)
bw.ReadFrom(s)
//bw.Flush() //ReadFrom无需使用Flush,其自己已经写入.
fmt.Println(b) // Hello 世界!
}

type ReadWriter

1
2
3
4
type ReadWriter struct {
*Reader
*Writer
}

ReadWriter类型保管了指向Reader和Writer类型的指针,(因此)实现了io.ReadWriter接口。

func NewReadWriter

1
func NewReadWriter(r *Reader, w *Writer) *ReadWriter

NewReadWriter申请创建一个新的、将读写操作分派给r和w 的ReadWriter。

1
2
3
4
5
6
7
8
9
10
11
12
func main() {
b := bytes.NewBuffer(make([]byte, 0))
bw := bufio.NewWriter(b)
s := strings.NewReader("123")
br := bufio.NewReader(s)
rw := bufio.NewReadWriter(br, bw)
p, _ := rw.ReadString('\n')
fmt.Println(string(p)) //123
rw.WriteString("asdf")
rw.Flush()
fmt.Println(b) //asdf
}

type SplitFunc

1
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)

SplitFunc类型代表用于对输出作词法分析的分割函数。

参数data是尚未处理的数据的一个开始部分的切片,参数atEOF表示是否Reader接口不能提供更多的数据。返回值是解析位置前进的字节数,将要返回给调用者的token切片,以及可能遇到的错误。如果数据不足以(保证)生成一个完整的token,例如需要一整行数据但data里没有换行符,SplitFunc可以返回(0, nil, nil)来告诉Scanner读取更多的数据写入切片然后用从同一位置起始、长度更长的切片再试一次(调用SplitFunc类型函数)。

如果返回值err非nil,扫描将终止并将该错误返回给Scanner的调用者。

除非atEOF为真,永远不会使用空切片data调用SplitFunc类型函数。然而,如果atEOF为真,data却可能是非空的、且包含着未处理的文本。

SplitFunc 的作用很简单,从 data 中找出你感兴趣的数据,然后返回并告诉调用者,data 中有多少数据你已经处理过了

func ScanBytes

1
func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)

ScanBytes是用于Scanner类型的分割函数(符合SplitFunc),本函数会将每个字节作为一个token返回。

func ScanRunes

1
func ScanRunes(data []byte, atEOF bool) (advance int, token []byte, err error)

ScanRunes是用于Scanner类型的分割函数(符合SplitFunc),本函数会将每个utf-8编码的unicode码值作为一个token返回。本函数返回的rune序列和range一个字符串的输出rune序列相同。错误的utf-8编码会翻译为U+FFFD = “\xef\xbf\xbd”,但只会消耗一个字节。调用者无法区分正确编码的rune和错误编码的rune。

func ScanWords

1
func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error)

ScanRunes是用于Scanner类型的分割函数(符合SplitFunc),本函数会将空白(参见unicode.IsSpace)分隔的片段(去掉前后空白后)作为一个token返回。本函数永远不会返回空字符串。用来找出 data 中的单行数据并返回(包括空行)

func ScanLines

1
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error)

ScanLines是用于Scanner类型的分割函数(符合SplitFunc),本函数会将每一行文本去掉末尾的换行标记作为一个token返回。返回的行可以是空字符串。换行标记为一个可选的回车后跟一个必选的换行符。最后一行即使没有换行符也会作为一个token返回。

type Scanner

1
2
3
4
5
6
7
8
9
10
type Scanner struct {
r io.Reader // The reader provided by the client.
split SplitFunc // The function to split the tokens.
maxTokenSize int // Maximum size of a token; modified by tests.
token []byte // Last token returned by split.
buf []byte // Buffer used as argument to split.
start int // First non-processed byte in buf.
end int // End of data in buf.
err error // Sticky error.
}

Scanner类型提供了方便的读取数据的接口,如从换行符分隔的文本里读取每一行。成功调用的Scan方法会逐步提供文件的token,跳过token之间的字节。token由SplitFunc类型的分割函数指定;默认的分割函数会将输入分割为多个行,并去掉行尾的换行标志。本包预定义的分割函数可以将文件分割为行、字节、unicode码值、空白分隔的word。调用者可以定制自己的分割函数。扫描会在抵达输入流结尾、遇到的第一个I/O错误、token过大不能保存进缓冲时,不可恢复的停止。当扫描停止后,当前读取位置可能会远在最后一个获得的token后面。需要更多对错误管理的控制或token很大,或必须从reader连续扫描的程序,应使用bufio.Reader代替。

func NewScanner

1
func NewScanner(r io.Reader) *Scanner

NewScanner创建并返回一个从r读取数据的Scanner,默认的分割函数是ScanLines。

func (*Scanner) Split

1
func (s *Scanner) Split(split SplitFunc)

Split设置该Scanner的分割函数。本方法必须在Scan之前调用。

1
2
3
4
5
6
7
8
func main() {
s := strings.NewReader("ABC DEF GHI JKL")
bs := bufio.NewScanner(s)
bs.Split(bufio.ScanWords)
for bs.Scan() {
fmt.Println(bs.Text())
}
}

func (*Scanner) Scan

1
func (s *Scanner) Scan() bool

Scan方法获取当前位置的token(该token可以通过Bytes或Text方法获得),并让Scanner的扫描位置移动到下一个token。当扫描因为抵达输入流结尾或者遇到错误而停止时,本方法会返回false。在Scan方法返回false后,Err方法将返回扫描时遇到的任何错误;除非是io.EOF,此时Err会返回nil。

1
2
3
4
5
6
7
8
func main() {
s := strings.NewReader("Hello 世界!")
bs := bufio.NewScanner(s)
bs.Split(bufio.ScanBytes)
for bs.Scan() {
fmt.Printf("%s ", bs.Text())
}
}

func (*Scanner) Bytes

1
func (s *Scanner) Bytes() []byte

Bytes方法返回最近一次Scan调用生成的token。底层数组指向的数据可能会被下一次Scan的调用重写。

1
2
3
4
5
6
7
8
func main() {
s := strings.NewReader("Hello 世界!")
bs := bufio.NewScanner(s)
bs.Split(bufio.ScanRunes)
for bs.Scan() {
fmt.Printf("%s ", bs.Text())
}
}

func (*Scanner) Text

1
func (s *Scanner) Text() string

Bytes方法返回最近一次Scan调用生成的token,会申请创建一个字符串保存token并返回该字符串。

func (*Scanner) Err

1
func (s *Scanner) Err() error

Err返回Scanner遇到的第一个非EOF的错误。

Golang标准库log

log简介

Golang内置了 log包,实现简单的日志服务。通过调用 log包的函数,可以实现简单的日志打印功能。

log使用

log包中有3个系列的日志打印函数,分别 print系列、panic系列、fatal系列。

函数系列 作用
print 单纯打印日志
panic 打印日志,抛出panic异常
fatal 打印日志,强制结束程序(os.Exit(1)),defer函数不会执行

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
"log"
)

func main() {
defer fmt.Println("发生了 panic错误!")
log.Print("my log")
log.Printf("my log %d", 100)
name := "tom"
age := 20
log.Println(name, ",", age)
log.Panic("致命错误!")
// log.Fatal("致命错误!")
fmt.Println("end...")
}

结果示例(实际结果不是这样的哦,因为panic,fatal会影响程序的执行):

1
2
3
4
5
6
7
8
9
10
11
12
13
2021/11/29 16:45:59 my log
2021/11/29 16:45:59 my log 100
2021/11/29 16:45:59 tom , 20
2021/11/29 16:45:59 致命错误!
发生了 panic错误!
panic: 致命错误!

goroutine 1 [running]:
log.Panic({0xc000107f00, 0x3, 0xc000107f00})
C:/Program Files/Go/src/log/log.go:354 +0x65
main.main()
c:/Users/52406/Desktop/golangprojects/xiaobaibk.com/pro01/demo.go:15 +0x19e
exit status 2

log配置

标准log配置

默认情况下log只会打印出时间,但是实际情况下我们可能还需要获取文件名,行号等信息,log包提供给我们定制的接口。 log包提供两个标准log配置的相关方法:

1
2
func Flags() int  // 返回标准log输出配置
func SetFlags(flag int) // 设置标准log输出配置

flag参数

1
2
3
4
5
6
7
8
9
10
11
const (
// 控制输出日志信息的细节,不能控制输出的顺序和格式。
// 输出的日志在每一项后会有一个冒号分隔:例如2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
Ldate = 1 << iota // 日期:2009/01/23
Ltime // 时间:01:23:23
Lmicroseconds // 微秒级别的时间:01:23:23.123123(用于增强Ltime位)
Llongfile // 文件全路径名+行号: /a/b/c/d.go:23
Lshortfile // 文件名+行号:d.go:23(会覆盖掉Llongfile)
LUTC // 使用UTC时间
LstdFlags = Ldate | Ltime // 标准logger的初始值
)

标准日志配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"fmt"
"log"
)

func main() {
i := log.Flags()
fmt.Printf("i: %v\n", i)
log.SetFlags(log.Ldate | log.Ltime | log.Llongfile)
log.Print("my log")
}

输出结果:

1
2
i: 3
2021/11/29 16:49:53 c:/Users/52406/Desktop/golangprojects/xiaobaibk.com/pro01/demo.go:12: my log

日志前缀配置

log包提供两个日志前缀配置的相关函数:

1
2
func Prefix() string  // 返回日志的前缀配置
func SetPrefix(prefix string) // 设置日志前缀

日志前缀配置实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
"log"
)

func main() {
s := log.Prefix()
fmt.Printf("s: %v\n", s)
log.SetPrefix("MyLog: ")
s = log.Prefix()
fmt.Printf("s: %v\n", s)
log.Print("my log...")
}

输出结果:

1
2
3
s: 
s: MyLog:
MyLog: 2021/11/29 16:51:55 my log...

日志输出位置配置

前面介绍的都是将日志输出到控制台上,golang的 log包还支持将日志输出到文件中。log包提供了 func SetOutput(w io.Writer)函数,将日志输出到文件中。

日志输出位置配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"log"
"os"
)

func main() {
f, err := os.OpenFile("a.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
log.Panic("打开日志文件异常")
}
log.SetOutput(f)
log.Print("my log...")
}

结果:日志输出到当前目录下a.log文件中

1
2021/11/29 16:57:13 my log...

自定义logger

log包为我们提供了内置函数,让我们能自定义logger。从效果上来看,就是将标题3中的标准日志配置、日志前缀配置、日志输出位置配置整合到一个函数中,使日志配置不在那么繁琐。 log包中提供了 func New(out io.Writer, prefix string, flag int) *Logger函数来实现自定义logger。

示例

1
2
3
4
5
6
7
8
9
10
11
12
var logger *log.Logger

func init() {
logFile, err := os.OpenFile("a.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
log.Panic("打开日志文件异常")
}
logger = log.New(logFile, "success", log.Ldate | log.Ltime | log.Lshortfile)
}
func main() {
logger.Println("自定义logger")
}

Golang标准库builtin

这个包提供了一些类型声明、变量和常量声明,还有一些便利函数,这个包不需要导入,这些变量和函数就可以直接使用。

常用函数

append

1
2
3
4
func append(slice []Type, elems ...Type) []Type

slice = append(slice, elem1, elem2)  //直接在slice后面添加单个元素,添加元素类型可以和slice相同,也可以不同
slice = append(slice, anotherSlice...)  //直接将另外一个slice添加到slice后面,但其本质还是将anotherSlice中的元素一个一个添加到slice中,和第一种方式类似.

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {

s1 := []int{1, 2, 3}
i := append(s1, 4)
fmt.Printf("i: %v\n", i)

s2 := []int{7, 8, 9}
i2 := append(s1, s2...)
fmt.Printf("i2: %v\n", i2)
}

len

返回,数组、切片、字符串、通道的长度

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {

s1 := "hello world"
i := len(s1)
fmt.Printf("i: %v\n", i)

s2 := []int{1, 2, 3}
fmt.Printf("len(s2): %v\n", len(s2))

}

print、println

打印输出到控制台。

实例

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
name := "tom"
age := 20
print(name, " ", age, "\n")
fmt.Println("----------")
println(name, " ", age)
}

panic

抛出一个panic异常

实例

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
defer fmt.Println("panic 异常后执行...")
panic("panic 错误...")
fmt.Println("end...")
}

new和make

newmake区别:

  1. make只能用来分配及初始化 类型为slicemapchan的数据;new可以分配任意类型的数据
  2. new分配返回的是指针,即类型*Tmake返回引用,即T
  3. new分配的空间被清零,make分配后,会进行初始化

实例

new

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func testNew() {
b := new(bool)
fmt.Println(*b)
i := new(int)
fmt.Println(*i)
s := new(string)
fmt.Println(*s)
}

func main() {
testNew()
}

运行结果:
false
0
""

make

内建函数make(T, args)与new(T)的用途不一样。它只用来创建slice,map和channel,并且返回一个初始化的(而不是置零),类型为T的值(而不是*T)。之所以有所不同,是因为这三个类型的背后引用了使用前必须初始化的数据结构。例如,slice是一个三元描述符,包含一个指向数据(在数组中)的指针,长度,以及容量,在这些项被初始化之前,slice都是nil的。对于slice,map和channel,make初始化这些内部数据结构,并准备好可用的值。

1
make([]int, 10, 100)

分配一个有100个int的数组,然后创建一个长度为10,容量为100的slice结构,该slice引用包含前10个元素的数组。对应的,new([]int)返回一个指向新分配的,被置零的slice结构体的指针,即指向值为nil的slice的指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var p *[]int = new([]int)       // allocates slice structure; *p == nil; rarely useful

var v []int = make([]int, 100) // the slice v now refers to a new array of 100 ints

// Unnecessarily complex:这种做法实在是很蛋疼

var p *[]int = new([]int)
*p = make([]int, 100, 100)

// Idiomatic:习惯的做法
v := make([]int, 100)

运行结果:
p: &[]
v: [0 0 0 0 0 0 0 0 0 0]

Golang标准库bytes

bytes包提供了对字节切片 进行读写操作的一系列函数,字节切片处理的函数比较多分为基本处理函数、比较函数、后缀检查函数、索引函数、分割函数、大小写处理函数和子切片处理函数等.

常用函数

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package main

import (
"bytes"
"fmt"
)

func main() {
var i int = 1
var j byte = 2
j = byte(i)
fmt.Printf("j: %v\n", j)

//Contains
b := []byte("xiaobaibk.com") //字符串强转为byte切片
sublice1 := []byte("xiaobaibk")
sublice2 := []byte("Xiaobaibk")
fmt.Println(bytes.Contains(b, sublice1)) //true
fmt.Println(bytes.Contains(b, sublice2)) //false

//Count
s := []byte("hellooooooooo")
sep1 := []byte("h")
sep2 := []byte("l")
sep3 := []byte("o")
fmt.Println(bytes.Count(s, sep1)) //1
fmt.Println(bytes.Count(s, sep2)) //2
fmt.Println(bytes.Count(s, sep3)) //9

//Repeat
b = []byte("hi")
fmt.Println(string(bytes.Repeat(b, 1))) //hi
fmt.Println(string(bytes.Repeat(b, 3))) //hihihi

//Replace
s = []byte("hello,world")
old := []byte("o")
news := []byte("ee")
fmt.Println(string(bytes.Replace(s, old, news, 0))) //hello,world
fmt.Println(string(bytes.Replace(s, old, news, 1))) //hellee,world
fmt.Println(string(bytes.Replace(s, old, news, 2))) //hellee,weerld
fmt.Println(string(bytes.Replace(s, old, news, -1))) //hellee,weerld

//Runes
s = []byte("你好世界")
r := bytes.Runes(s)
fmt.Println("转换前字符串的长度: ", len(s)) //12
fmt.Println("转换后字符串的长度: ", len(r)) //4

//Join
s2 := [][]byte{[]byte("你好"), []byte("世界")}
sep4 := []byte(",")
fmt.Println(string(bytes.Join(s2, sep4))) //你好,世界
sep5 := []byte("#")
fmt.Println(string(bytes.Join(s2, sep5))) //你好#世界
}

Buffer类型

缓冲区是具有读取和写入方法的可变大小的字节缓冲区。Buffer 的零值是准备使用的空缓冲区。

声明一个Buffer的四种方法:

1
2
3
4
var b bytes.Buffer // 直接定义一个Buffer变量,不用初始化,可以直接使用
b := new(bytes.Buffer) //使用New返回Buffer变量
b := bytes.NewBuffer(s []byte) //从一个[]byte切片,构造一个Buffer
b := bytes.NewBufferString(s string) //从一个string变量,构造一个Buffer

往Buffer中写入数据

1
2
3
4
5
b.Write(d []byte) //将切片d写入Buffer尾部
b.WriteString(s string) //将字符串s写入Buffer尾部
b.WriteByte(c byte) //将字符c写入Buffer尾部
b.WriteRune(r rune) //将一个rune类型的数据放到缓冲器的尾部
b.WriteTo(w io.Writer) //将Buffer中的内容输出到实现了io.Writer接口的可写入对象中

注:将文件中的内容写入Buffer,则使用ReadForm(i io.Reader)

从Buffer中读取数据到指定容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
c := make([]byte,8)
b.Read(c) //一次读取8个byte到c容器中,每次读取新的8个byte覆盖c中原来的内容
b.ReadByte() //读取第一个byte,b的第1个byte被拿掉,赋值给 a => a, _ := b.ReadByte()
b.ReadRune() //读取第一个rune,b的第1个rune被拿掉,赋值给 r => r, _ := b.ReadRune()
b.ReadBytes(delimiter byte) //需要一个 byte作为分隔符 ,读的时候从缓冲器里找第一个出现的分隔符(delim),找到后,把从缓冲器头部开始到分隔符之间的所有byte进行返回,作为byte类型的slice,返回后,缓冲器也会空掉一部分
b.ReadString(delimiter byte) // 需要一个byte作为分隔符,读的时候从缓冲器里找第一个出现的分隔符(delim),找到后,把从缓冲器头部开始到分隔符之间的所有byte进行返回, 作为字符串返回 ,返回后,缓冲器也会空掉一部分b.ReadForm(i io.Reader) // 从一个实现io.Reader接口的r,把r里的内容读到缓冲器里 ,n 返回读的数量

file, _ := os.Open(".text.txt")
buf := bytes.NewBufferString("Hello world")
buf.ReadFrom(file)
//将text.txt内容追加到缓冲器的尾部
fmt.Println(buf.String())
清空数据
b.Reset()
转换为字符串
b.String()

Reader类型

Reader实现了 io.Reader, io.ReaderAt, io.WriterTo, io.Seeker,io.ByteScanner, io.RuneScanner接口,Reader是只读的、可以seek。

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
36
37
38
39
40
41
42
43
44
45
46
func testReader() {
data := "123456789"
//通过[]byte创建Reader
re := bytes.NewReader([]byte(data))
//返回未读取部分的长度
fmt.Println("re len : ", re.Len())
//返回底层数据总长度
fmt.Println("re size : ", re.Size())
fmt.Println("------------")

buf := make([]byte, 2)
for {
//读取数据
n, err := re.Read(buf)
if err != nil {
break
}
fmt.Println(string(buf[:n]))
}

fmt.Println("------------")

//设置偏移量,因为上面的操作已经修改了读取位置等信息
re.Seek(0, 0)
for {
//一个字节一个字节的读
b, err := re.ReadByte()
if err != nil {
break
}
fmt.Println(string(b))
}
fmt.Println("------------")

re.Seek(0, 0)
off := int64(0)
for {
//指定偏移量读取
n, err := re.ReadAt(buf, off)
if err != nil {
break
}
off += int64(n)
fmt.Println(off, string(buf[:n]))
}
}

Golang标准库errors

errors包实现了操作错误的函数。语言使用 error 类型来返回函数执行过程中遇到的错误,如果返回的 error 值为 nil,则表示未遇到错误,否则 error 会返回一个字符串,用于说明遇到了什么错误。

error 结构

1
2
3
type error interface {
Error() string
}

你可以用任何类型去实现它(只要添加一个 Error() 方法即可),也就是说,error 可以是任何类型,这意味着,函数返回的 error 值实际可以包含任意信息,不一定是字符串。

error 不一定表示一个错误,它可以表示任何信息,比如 io 包中就用 error 类型的 io.EOF 表示数据读取结束,而不是遇到了什么错误。

errors 包实现了一个最简单的 error 类型,只包含一个字符串,它可以记录大多数情况下遇到的错误信息。errors 包的用法也很简单,只有一个 New 函数,用于生成一个最简单的 error 对象:

1
func New(text string) error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"errors"
"fmt"
)

func check(s string) error {
if s == "" {
return errors.New("字符串不能为空")
} else {
return nil
}
}

func main() {
check("hello")
err := check("")
fmt.Printf("err: %v\n", err.Error())
}

自定义错误

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
package main

import (
"fmt"
"time"
)
// MyError is an error implementation that includes a time and message.
type MyError struct {
When time.Time
What string
}

func (e MyError) Error() string {
return fmt.Sprintf("%v: %v", e.When, e.What)
}

func oops() error {
return MyError{
time.Date(1989, 3, 15, 22, 30, 0, 0, time.UTC),
"the file system has gone away",
}
}
func main() {
if err := oops(); err != nil {
fmt.Println(err)
}
}

Golang标准库中的sort包

sort包的内容,以及使用

sort包提供了排序切片和用户自定义数据集以及相关功能的函数。

sort包主要针对 []int[]float64[]string、以及其他自定义切片 的排序。

结构体

1
2
3
type IntSlice []int
type Float64Slice
type StringSlice

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func Ints(a []int)
func IntsAreSorted(a []int) bool
func SearchInts(a []int, x int) int
func Float64s(a []float64)
func Float64sAreSorted(a []float64) bool
func SearchFloat64s(a []float64, x float64) int
func SearchFloat64s(a []flaot64, x float64) bool
func Strings(a []string)
func StringsAreSorted(a []string) bool
func SearchStrings(a []string, x string) int
func Sort(data Interface)
func Stable(data Interface)
func Reverse(data Interface) Interface
func ISSorted(data Interface) bool
func Search(n int, f func(int) bool) int

接口 type Interface

1
2
3
4
5
type Interface interface {
Len() int // Len方法返回集合中的元素个数
Less(i, j int) bool // i>j,该方法返回索引i的元素是否比索引j的元素小、
Swap(i, j int) // 交换i, j的值
}

实例

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
package main

import (
"fmt"
"sort"
)

type NewInts []uint

func (n NewInts) Len() int {
return len(n)
}

func (n NewInts) Less(i, j int) bool {
fmt.Println(i, j, n[i] < n[j], n)
return n[i] < n[j]
}

func (n NewInts) Swap(i, j int) {
n[i], n[j] = n[j], n[i]
}

func main() {
n := []uint{1,3,2}
sort.Sort(NewInts(n))
fmt.Println(n)
}

结构体

三种结构体的方法都是一样的,只是分别针对int切片、float64切片、strings切片这三种不同的类型。 然后三种结果都有五个公开方法

1
2
3
4
5
6
func (p xxxSlice) Len() int  // 切片长度
func (p xxxSlice) Less(i, j int) bool
func (p xxxSlice) Swap(i, j int)
func (p xxxSlice) Search(x xxx) int
// 这个和后面那个功能一样
func (p xxxSlice) Sort()

综合实例

[]float64:

1
2
3
4
f := []float64{1.1, 4.4, 5.5, 3.3, 2.2}
sort.Float64s(f)
fmt.Printf("f: %v\n", f)
// f: [1.1 2.2 3.3 4.4 5.5]

[]int:

1
2
3
4
f := []int{3, 5, 1, 2, 4}
sort.Ints(f)
fmt.Printf("f: %v\n", f)
// f: [1 2 3 4 5]

string:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//字符串排序,现比较高位,相同的再比较低位
// [] string
ls := sort.StringSlice{
"100",
"42",
"41",
"3",
"2",
}
fmt.Println(ls) //[100 42 41 3 2]
sort.Strings(ls)
fmt.Println(ls) //[100 2 3 41 42]


//字符串排序,现比较高位,相同的再比较低位
ls := sort.StringSlice{
"d",
"ac",
"c",
"ab",
"e",
}
fmt.Println(ls) //[d ac c ab e]
sort.Strings(ls)
fmt.Println(ls) //[ab ac c d e]


//汉字排序,依次比较byte大小
ls := sort.StringSlice{
"啊",
"博",
"次",
"得",
"饿",
"周",
}
fmt.Println(ls) //[啊 博 次 得 饿 周]
sort.Strings(ls)
fmt.Println(ls) //[博 周 啊 得 次 饿]

for _, v := range ls{
fmt.Println(v, []byte(v))
}

//博 [229 141 154]
//周 [229 145 168]
//啊 [229 149 138]
//得 [229 190 151]
//次 [230 172 161]
//饿 [233 165 191]

复杂结构:[][]int :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type testSlice [][]int

func (l testSlice) Len() int { return len(l) }
func (l testSlice) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l testSlice) Less(i, j int) bool { return l[i][1] < l[j][1] }

func main() {
ls := testSlice{
{1,4},
{9,3},
{7,5},
}

fmt.Println(ls) //[[1 4] [9 3] [7 5]]
sort.Sort(ls)
fmt.Println(ls) //[[9 3] [1 4] [7 5]]
}

复杂结构体:[]map[string]int [{"k":0},{"k1":1},{"k2":2] :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type testSlice []map[string]float64

func (l testSlice) Len() int { return len(l) }
func (l testSlice) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l testSlice) Less(i, j int) bool { return l[i]["a"] < l[j]["a"] } //按照"a"对应的值排序

func main() {
ls := testSlice{
{"a":4, "b":12},
{"a":3, "b":11},
{"a":5, "b":10},
}

fmt.Println(ls) //[map[a:4 b:12] map[a:3 b:11] map[a:5 b:10]]
sort.Sort(ls)
fmt.Println(ls) //[map[a:3 b:11] map[a:4 b:12] map[a:5 b:10]]
}

复杂结构体:[]struct :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type People struct {
Name string
Age int
}

type testSlice []People

func (l testSlice) Len() int { return len(l) }
func (l testSlice) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l testSlice) Less(i, j int) bool { return l[i].Age < l[j].Age }

func main() {
ls := testSlice{
{Name:"n1", Age:12},
{Name:"n2", Age:11},
{Name:"n3", Age:10},
}

fmt.Println(ls) //[{n1 12} {n2 11} {n3 10}]
sort.Sort(ls)
fmt.Println(ls) //[{n3 10} {n2 11} {n1 12}]
}

Golang标准库time

time包提供测量和显示时间的功能。

基本使用

打印显示出现在的时间,基本示例如下。 其中now为 time.Time类型,Month 为 time.Month类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func test1() {
now := time.Now() //获取当前时间
// current time:2020-12-01 22:24:30.85736 +0800 CST m=+0.000096031
fmt.Printf("current time:%v\n", now)
year := now.Year() //年
month := now.Month() //月
day := now.Day() //日
hour := now.Hour() //小时
minute := now.Minute() //分钟
second := now.Second() //秒
fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second)
fmt.Printf("%T,%T,%T,%T,%T,%T,%T\n", now, year, month, day, hour, minute, second)
// time.Time,int,time.Month,int,int,int,int
}

时间戳

在编程中对于时间戳的应用也尤为广泛,例如在Web开发中做cookies有效期,接口加密,Redis中的key有效期等等,大部分都是使用到了时间戳。

时间戳是自1970年1月1日(08:00:00GMT)至当前时间的总毫秒数。它也被称为Unix时间戳(UnixTimestamp)。 在GoLang中,获取时间戳的操作如下

1
2
3
4
5
func test2() {
now := time.Now()
// 当前时间戳 TimeStamp type:int64, TimeStamp:1606832965
fmt.Printf("TimeStamp type:%T, TimeStamp:%v", now.Unix(), now.Unix())
}

除此之外还有纳秒时间戳,我们可以使用 time.Now().UnixNano()来获取它

1
2
3
4
5
func test3() {
now := time.Now()
// 纳秒级时间戳TimeStamp type:int64, TimeStamp:1606833059999670000
fmt.Printf("TimeStamp type:%T, TimeStamp:%v\n", now.UnixNano(), now.UnixNano())
}

时间戳转化为普通的时间格式

go语言中可以 time.Unix来直接将时间戳转化为当前时间格式,实现瞬间替换。

1
2
3
4
5
6
7
8
9
10
11
12
func timeStampToTime() {
timestamp := time.Now().Unix()
timeObj := time.Unix(timestamp, 0) //将时间戳转为时间格式
fmt.Println(timeObj)
year := timeObj.Year() //年
month := timeObj.Month() //月
day := timeObj.Day() //日
hour := timeObj.Hour() //小时
minute := timeObj.Minute() //分钟
second := timeObj.Second() //秒
fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second)
}

操作时间

ADD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
"time"
)

func add(h, m, s, mls, msc, ns time.Duration) {
now := time.Now()
fmt.Println(now.Add(time.Hour*h + time.Minute*m + time.Second*s + time.Millisecond*mls + time.Microsecond*msc + time.Nanosecond*ns))
}

func main() {
test4(3, 4, 5, 6, 7, 8)
}

注意在这里并不能增加年\月\日,仅能增加时分秒,也就是以下的才被允许

1
2
3
4
5
6
7
8
const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
)

Sub

1
2
3
4
5
6
func sub() {
now := time.Now()
targetTime := now.Add(time.Hour)
// 目标时间与此时相比相差1h0m0s
fmt.Println(targetTime.Sub(now))
}

谁的sub谁为参照时间

Equal

1
func (t Time) Equal(u Time) bool

判断两个时间是否相同,会考虑时区的影响,因此不同时区标准的时间也可以正确比较。

Before

1
func (t Time) Before(u Time) bool

如果t代表的时间点在u之前,返回真;否则返回假。

After

1
func (t Time) After(u Time) bool

如果t代表的时间点在u之后,返回真;否则返回假。

定时器

使用 time.Tick(时间间隔)来设置定时器,定时器的本质上是一个通道(channel)。

1
2
3
4
5
6
func tick() {
ticker := time.Tick(time.Second) //定义一个1秒间隔的定时器
for i := range ticker {
fmt.Println(i)//每秒都会执行的任务
}
}

时间格式化

时间类型有一个自带的方法 Format进行格式化,需要注意的是Go语言中格式化时间模板不是常见的 Y-m-d H:M:S而是使用Go的诞生时间2006年1月2号15点04分(记忆口诀为2006 1 2 3 4)

1
2
3
4
5
6
7
8
9
10
11
func format() {
now := time.Now()
// 格式化的模板为Go的出生时间2006年1月2号15点04分 Mon Jan
// 24小时制
fmt.Println(now.Format("2006-01-02 15:04:05.000 Mon Jan"))
// 12小时制
fmt.Println(now.Format("2006-01-02 03:04:05.000 PM Mon Jan"))
fmt.Println(now.Format("2006/01/02 15:04"))
fmt.Println(now.Format("15:04 2006/01/02"))
fmt.Println(now.Format("2006/01/02"))
}

补充:如果想格式化为12小时方式,需指定 PM

解析字符串格式的时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
now := time.Now()
fmt.Println(now)
// 加载时区
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
fmt.Println(err)
return
}
// 按照指定时区和指定格式解析字符串时间
timeObj, err := time.ParseInLocation("2006/01/02 15:04:05", "2019/08/04 14:15:20", loc)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(timeObj)
fmt.Println(timeObj.Sub(now))

Golang 标准库encoding/json

这个包可以实现json的编码和解码,就是将json字符串转换为 struct,或者将 struct转换为json。

核心的两个函数

1
func Marshal(v interface{}) ([]byte, error)

将struct编码成json,可以接收任意类型

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

将json转码成struct结构体

两个核心结构体

1
2
3
type Decoder struct {
// contains filtered or unexported fields
}

从输入流读取并解析json

1
2
3
type Encoder struct {
// contains filtered or unexported fields
}

写json到输出流

实例演示

结构体转换为json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type Person struct {
Name string
Age int
Email string
}

func Marshal() {
p := Person{
Name: "tom",
Age: 20,
Email: "[email protected]",
}
b, _ := json.Marshal(p)
fmt.Printf("b: %v\n", string(b))
}

json转换为结构体

1
2
3
4
5
6
func Unmarshal() {
b1 := []byte(`{"Name":"tom","Age":20,"Email":"[email protected]"}`)
var m Person
json.Unmarshal(b1, &m)
fmt.Printf("m: %v\n", m)
}

解析嵌套类型

1
2
3
4
5
6
7
// 解析嵌套类型
func test3() {
b := []byte(`{"Name":"tom","Age":20,"Email":"[email protected]", "Parents":["tom", "kite"]}`)
var f interface{}
json.Unmarshal(b, &f)
fmt.Printf("f: %v\n", f)
}

解析嵌套引用类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func test4() {
type Person struct {
Name string
Age int
Email string
Parent []string
}

p := Person{
Name: "tom",
Age: 20,
Email: "[email protected]",
Parent: []string{"big tom", "big kite"},
}

b, _ := json.Marshal(p)
fmt.Printf("b: %v\n", string(b))
}

io流 Reader Writer 可以扩展到http websocket等场景

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
func test5() {
// dec := json.NewDecoder(os.Stdin)
// a.json : {"Name":"tom","Age":20,"Email":"[email protected]", "Parents":["tom", "kite"]}
f, _ := os.Open("a.json")
dec := json.NewDecoder(f)
enc := json.NewEncoder(os.Stdout)
for {
var v map[string]interface{}
if err := dec.Decode(&v); err != nil {
log.Println(err)
return
}
fmt.Printf("v: %v\n", v)
if err := enc.Encode(&v); err != nil {
log.Println(err)
}
}

/*
输入 {"Name":"tom","Age":20,"Email":"[email protected]"}
输出
v: map[Age:20 Email:[email protected] Name:tom]
{"Age":20,"Email":"[email protected]","Name":"tom"}
*/
}

也可以读写文件

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
func test6() {
f, _ := os.Open("a.json")
defer f.Close()
d := json.NewDecoder(f)
var v map[string]interface{}
d.Decode(&v)

fmt.Printf("v: %v\n", v)
}

func test7() {
type Person struct {
Name string
Age int
Email string
Parent []string
}

p := Person{
Name: "tom",
Age: 20,
Email: "[email protected]",
Parent: []string{"big tom", "big kite"},
}

f, _ := os.OpenFile("a.json", os.O_WRONLY, 0777)
defer f.Close()
e := json.NewEncoder(f)
e.Encode(p)
}

Golang 标准库encoding/xml

xml包实现xml解析

核心的两个函数

1
func Marshal(v interface{}) ([]byte, error)

将struct编码成xml,可以接收任意类型

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

将xml转码成struct结构体

两个核心结构体

1
2
3
type Decoder struct {
...
}

从输入流读取并解析xml

1
2
3
type Encoder  struct {
// contains filtered or unexported fields
}

写xml到输出流

实例演示

结构体转换为xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type Person struct {
XMLName xml.Name `xml:"person"`
Name string `xml:"name"`
Age int `xml:"age"`
Email string `xml:"email"`
}

func Marshal() {
p := Person{
Name: "tom",
Age: 20,
Email: "[email protected]",
}
// b, _ := xml.Marshal(p)
// 有缩进格式
b, _ := xml.MarshalIndent(p, " ", " ")
fmt.Printf("%v\n", string(b))
}

也可以读写文件

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
func read() {
/*
<?xml version="1.0" encoding="UTF-8"?>
<person>
<name>tom</name>
<age>20</age>
<email>[email protected]</email>
</person>
*/
b, _ := ioutil.ReadFile("a.xml")
var p Person
xml.Unmarshal(b, &p)
fmt.Printf("p: %v\n", p)
}

func write() {
type Person struct {
XMLName xml.Name `xml:"person"`
Name string `xml:"name"`
Age int `xml:"age"`
Email string `xml:"email"`
}

p := Person{
Name: "tom",
Age: 20,
Email: "[email protected]",
}

f, _ := os.OpenFile("a.xml", os.O_WRONLY, 0777)
defer f.Close()
e := xml.NewEncoder(f)
e.Encode(p)
}

Golang标准库math

该包包含一些常量和一些有用的数学计算函数,例如:三角函数、随机数、绝对值、平方根等。

常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fmt.Printf("float64的最大值是:%.f\n", math.MaxFloat64)
fmt.Printf("float64的最小值是:%.f\n", math.SmallestNonzeroFloat64)
fmt.Printf("float32的最大值是:%.f\n", math.MaxFloat32)
fmt.Printf("float32的最小值是:%.f\n", math.SmallestNonzeroFloat32)
fmt.Printf("Int8的最大值是:%d\n", math.MaxInt8)
fmt.Printf("Int8的最小值是:%d\n", math.MinInt8)
fmt.Printf("Uint8的最大值是:%d\n", math.MaxUint8)
fmt.Printf("Int16的最大值是:%d\n", math.MaxInt16)
fmt.Printf("Int16的最小值是:%d\n", math.MinInt16)
fmt.Printf("Uint16的最大值是:%d\n", math.MaxUint16)
fmt.Printf("Int32的最大值是:%d\n", math.MaxInt32)
fmt.Printf("Int32的最小值是:%d\n", math.MinInt32)
fmt.Printf("Uint32的最大值是:%d\n", math.MaxUint32)
fmt.Printf("Int64的最大值是:%d\n", math.MaxInt64)
fmt.Printf("Int64的最小值是:%d\n", math.MinInt64)
fmt.Printf("圆周率默认为:%.200f\n", math.Pi)

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
float64的最大值是:179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368
float64的最小值是:0
float32的最大值是:340282346638528859811704183484516925440
float32的最小值是:0
Int8的最大值是:127
Int8的最小值是:-128
Uint8的最大值是:255
Int16的最大值是:32767
Int16的最小值是:-32768
Uint16的最大值是:65535
Int32的最大值是:2147483647
Int32的最小值是:-2147483648
Uint32的最大值是:4294967295
Int64的最大值是:9223372036854775807
Int64的最小值是:-9223372036854775808
圆周率默认为:3.14159265358979311599796346854418516159057617187500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

常用函数

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package main

import (
"fmt"
"math"
)

func main() {
/*
取绝对值,函数签名如下:
func Abs(x float64) float64
*/
fmt.Printf("[-3.14]的绝对值为:[%.2f]\n", math.Abs(-3.14))

/*
取x的y次方,函数签名如下:
func Pow(x, y float64) float64
*/
fmt.Printf("[2]的16次方为:[%.f]\n", math.Pow(2, 16))

/*
取余数,函数签名如下:
func Pow10(n int) float64
*/
fmt.Printf("10的[3]次方为:[%.f]\n", math.Pow10(3))

/*
取x的开平方,函数签名如下:
func Sqrt(x float64) float64
*/
fmt.Printf("[64]的开平方为:[%.f]\n", math.Sqrt(64))

/*
取x的开立方,函数签名如下:
func Cbrt(x float64) float64
*/
fmt.Printf("[27]的开立方为:[%.f]\n", math.Cbrt(27))

/*
向上取整,函数签名如下:
func Ceil(x float64) float64
*/
fmt.Printf("[3.14]向上取整为:[%.f]\n", math.Ceil(3.14))

/*
向下取整,函数签名如下:
func Floor(x float64) float64
*/
fmt.Printf("[8.75]向下取整为:[%.f]\n", math.Floor(8.75))

/*
取余数,函数签名如下:
func Floor(x float64) float64
*/
fmt.Printf("[10/3]的余数为:[%.f]\n", math.Mod(10, 3))

/*
分别取整数和小数部分,函数签名如下:
func Modf(f float64) (int float64, frac float64)
*/
Integer, Decimal := math.Modf(3.14159265358979)
fmt.Printf("[3.14159265358979]的整数部分为:[%.f],小数部分为:[%.14f]\n", Integer, Decimal)
}

随机数

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
package main

import (
"fmt"
"math/rand"
"time"
)

func init() {
//以时间作为初始化种子
rand.Seed(time.Now().UnixNano())
}
func main() {

for i := 0; i < 10; i++ {
a := rand.Int()
fmt.Println(a)
}

fmt.Println("------------")
for i := 0; i < 10; i++ {
a := rand.Intn(100)
fmt.Println(a)
}
fmt.Println("------------")
for i := 0; i < 10; i++ {
a := rand.Float32()
fmt.Println(a)
}
}