一、文件

打开关闭

1
2
file,err:= os.open("test.txt")
defer file.Close()

带缓冲的读取

默认的相对路径是工程下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func bufferReader(){
file, err := os.Open("./src/file/d.txt")
if err != nil {
fmt.Println("open file err=", err)
return
}
defer file.Close()
/**
创建一 *Reader, 是带缓冲的
defaultBufSize = 4096 //默认缓冲为4096
*/
reader := bufio.NewReader(file)
for {
str, err := reader.ReadString('\n') //读到换行结束
if err == io.EOF { //表示文件的末尾
fmt.Print(str)
break
}
//输出内容
fmt.Print(str)
}
fmt.Println("文件读取结束")
}

一次性读取文件

1
2
3
4
5
6
7
8
9
10
func readAll(){
file := "./src/file/d.txt"
//读取到的是一个byte[],封装了open和close
content, err:= ioutil.ReadFile(file)
if err != nil {
fmt.Println("read file err=", err)
return
}
fmt.Printf("%v", string(content))
}

写文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func writeFile(){
filePath := "./src/file/a.txt"
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Println("open file err=", err)
return
}
str := "hello,world"
//写入时,使用带缓存的 *Writer
writer := bufio.NewWriter(file)
writer.WriteString(str)
//带缓存,没有写入
writer.Flush()
}

文件打开方式

1
2
3
4
5
6
7
8
9
10
11
12
const (
// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
O_RDONLY int = syscall.O_RDONLY // open the file read-only.
O_WRONLY int = syscall.O_WRONLY // open the file write-only.
O_RDWR int = syscall.O_RDWR // open the file read-write.
// The remaining values may be or'ed in to control behavior.
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
O_CREATE int = syscall.O_CREAT // create a new file if none exists.
O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist.
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened.
)

判断文件是否存在

golang判断文件或文件夹是否存在的方法为使用os.stat()函数返回的错误值进行判断:

  1. 如果返回的错误为nil,说明文件或文件夹存在
  2. 如果返回的错误类型使用os.IsNotExist()判断为true,说明文件或文件夹不存在
  3. 如果返回的错误为其它类型,则不确定是否在存在
1
2
3
4
5
6
7
8
9
10
func PathExists(path string)(bool,error) {
err := os.Stat(path)
if err == nil {
return true, nil
}
if os.lsNotExist(err) {
return false, nil
}
return false, err
}

拷贝文件

可以完成包括图片视频的拷贝

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 CopyFile(dstFileName string, srcFileName string) (written int64, err error){
//打开源文件
srcfile, err := os.Open(srcFileName)
if err != nil {
fmt.Printf("open file err=%v\n", err)
return
}
defer srcfile.Close()
reader:= bufio.NewReader(srcfile)
//目标文件
dstFile, err := os.OpenFile(dstFileName, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Printf("open file err=%v\n", err)
return
}
defer dstFile.Close()
writer := bufio.NewWriter(dstFile)
return io.Copy(writer, reader)
}

func testCopy(){
srcFile := "./src/file/d.txt"
dstFile := "./src/file/b.txt"
_, err := CopyFile(dstFile, srcFile)
if err == nil {
fmt.Println("拷贝完成")
}else {
fmt.Printf("open file err=%v\n", err)
}
}

统计字符

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
type CharCount struct {
ChCount int //记录英文
NumCount int //数字
SpaceCount int //记录空格
OtherCount int //其他字符
}
func charCount(){
//打开源文件
fileName := "./src/file/d.txt"
srcfile, err := os.Open(fileName)
if err != nil {
fmt.Printf("open file err=%v\n", err)
return
}
defer srcfile.Close()

var count CharCount
reader := bufio.NewReader(srcfile)
//循环读取
for {
str, err := reader.ReadString('\n')
if err == io.EOF {
break
}
for _, v := range str{
switch {
case v >= 'a' && v <= 'z':
fallthrough
case v >= 'A' && v <= 'Z':
count.ChCount++
case v == ' ' || v == '\t':
count.SpaceCount++
case v >= '0' && v <= '9':
count.NumCount++
default:
count.OtherCount++
}
}
}
fmt.Printf("ChCount=%v, SpaceCount=%v, NumCount=%v, OtherCount=%v",
count.ChCount,count.SpaceCount,count.NumCount,count.OtherCount)
}

命令行参数

flag包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func args(){
var user string
var pwd string
var host string
var port int

//接收命令行参数 -u后面的参数值
flag.StringVar(&user, "u", "","用户名,默认为空" )
flag.StringVar(&pwd, "p", "","密码,默认为空" )
flag.StringVar(&host, "h", "localhost","主机,默认为localhost" )
flag.IntVar(&port, "port", 3306,"端口,默认为3306" )
//必须调用,转换
flag.Parse()
fmt.Printf("user=%v,pwd=%v,host=%v,port=%v",user,pwd,host,port)
}

调用

1
2
3
go build -o test.exe start.go args.go
test.exe -u a -p 1
user=a,pwd=1,host=localhost,port=3306

序列化操作

将有key-value结构的数据类型(比如结构体、map、切片序列化成json字符串

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

func testJson(){
student := Student{
Name: "a",
Age: 18,
}
data, err := json.Marshal(&student)
if err != nil {
fmt.Printf("序列化错误,err=%v",err)
}
fmt.Println(string(data))
}

使用tag将字段转换的名称更改,这里使用了反射机制

1
2
3
4
type Student struct {
Name string `json:"name"`
Age int `json:"age"`
}

反序列化,

1
2
3
4
5
6
7
8
9
10
func unmarshalStruct(){
str := "{\"name\":\"a\",\"age\":18}"
var student Student
err := json.Unmarshal([]byte(str), &student)
if err != nil {
fmt.Printf("序列化错误,err=%v",err)
}
fmt.Println(student)
}
//{a 18}

二、单元测试

Go语言中自带有一个轻量级的测试框架testing和自带的go test命令来实现单元测试和性能测试,testing框架和其他语言中的测试框架类似,可以基于这个框架写针对相应函数的测试用例,也可以基于该框架写相应的压力测试用例。通过单元测试,可以解决如下问题:

  1. 确保每个函数是可运行,并且运行结果是正确的
  2. 确保写出来的代码性能是好的,
  3. 单元测试能及时的发现程序设计或实现的逻辑错误,使问题及早暴露,便于问题的定位解决,而性能测试的重点在于发现程序设计上的一些问题,让程序能够在高并发的情况下还能保持稳定
1
func TestXXX(t *testing.T){}

要编写一个新的测试套件,需要创建一个名称以_test go结尾的文件,该文件包含TestXxx函数,如上所述。将该文件放在与被测试的包相同的包中,该文件将被排除在正常的程序包之外,但在运行go test命令时将被包含。有关详细信息,
如果有需要,可以调用T和*B的Skip方法,跳过该测试或基准测试︰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import (
"testing"
)

//被测试函数
func addUpper(n int) int {
res := 0
for i := 1; i <= n;i++{
res +=i
}
return res
}

func TestAddUpper(t *testing.T){
res := addUpper(10)
if res != 55 {
t.Fatalf("AddUpper执行失败,期望值=%v, 实际值=%v",55, res)
}
//正确
t.Logf("AddUpper执行成功")
}

将文件写在cal_test中

1
2
3
4
5
6
go test -v
=== RUN TestAddUpper
cal_test.go:21: AddUpper执行成功
--- PASS: TestAddUpper (0.00s)
PASS
ok unittest 0.389s

三、反射

基本介绍

反射可以用来做适配器方法,在框架中特别多

1
2
3
4
5
6
7
8
9
10
11
12
13
testi = func(v1 int, v2 int) {
t.Log(v1,v2)
}
test2 := func(v1 int, v2 int, s string) {
t.Log(v1,v2,s)
}
bridge= fund(call interfacet,args ...interface{}){//内容
W√
}
//实现调用test1对应的函数
bridge(test1,1,2)
//实现调用test2对应的函数
bridge(test2,1,2, "test2")
  1. 反射可以在运行时动态获取变量的各种信息,比如变量的类型(type),类别(kind)
  2. 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
  3. 通过反射,可以修改变量的值,可以调用关联的方法。
  • reflect.Type(v)返回接口,获取变量类型
  • reflect.Value(v)返回结构体,获取变量的值

变量、interface{}reflect.Value是可以相互转换的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//变量
var student stu

//专门用于做反射,将student传入
func test(b interface{}){
//将interface转成reflect.Value
rVal := reflect.ValueOf(b)

//2.reflect.Value转为interface{}
iVal := rVal.interface()

//3.将interface{}转成原理的变量类型,类型断言
v := iVal.(Stu)
}

操作

基本类型操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func reflectTest(b interface{}){
//获取传入变量的type,kind
rT := reflect.TypeOf(b)
fmt.Println("rType =",rT) //rType = int

rV := reflect.ValueOf(b)
fmt.Println("rValue =",rV) //rValue = 100

//获取具体值
iV := rV.Interface()
num := iV.(int)
fmt.Println(num)
}

func test(){
var num int = 100
reflectTest(num)
}

结构体操作

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
type Student struct {
Name string
Age int
}

func reflectTest02(b interface{}){
//1.获取传入变量的type
rT := reflect.TypeOf(b)
fmt.Println("rType =",rT)

//2.获取传入变量的value
rV := reflect.ValueOf(b)
fmt.Println("rValue =",rV)

//3.获取传入变量的Kind
fmt.Printf("kind1=%v, kind2=%v \n", rT.Kind(), rV.Kind())

iV := rV.Interface()
fmt.Printf("iv = %v, iV = %T", iV, iV)
}

func test(){
stu := Student{
Name : "tom",
Age : 18 ,
}
reflectTest02(stu)
}

修改变量

Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的value封装。如果v的Kind不是Interface或Ptr会panic;如果v持有的值为nil,会返回value零值。

1
2
3
4
5
6
7
8
9
10
11
func test02(){
var num int = 100
modifyNum(&num)
fmt.Println(num)
}

func modifyNum(b interface{}){
rV := reflect.ValueOf(b)
fmt.Printf("kind=%v\n", rV.Kind()) //ptr
rV.Elem().SetInt(20)
}

反射实践

  1. 通过反射变量结构体的字段,调研结构体方法,并获得结构体标签的值
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
type Monster struct {
Name string `json:"name"`
Age int `json:"age"`
Score float32 `json:"score"`
Sex string `json:"sex"`
}
func (s Monster) Print(){
fmt.Println("-----start----")
fmt.Println(s)
fmt.Println("-----end----")
}

func (s Monster) GetSum(n1, n2 int) int{
return n1 + n2
}
func (s Monster) Set(name string, age int,
score float32, sex string) {
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
}

func TestStruct(a interface{}){
typ := reflect.TypeOf(a)
val := reflect.ValueOf(a)
kd := val.Kind()
if kd != reflect.Struct {
fmt.Println("expect struct")
return
}

//结构体字段
num := val.NumField()
for i := 0; i < num; i++ {
fmt.Printf("Field %d = %v \n", i, val.Field(i))
fmt.Printf("Field %d:%v tag=%v \n", i, typ.Field(i), typ.Field(i).Tag.Get("json"))
}

method := val.NumMethod()
fmt.Printf("methodNum=%v\n", method)
var params []reflect.Value
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(20))
//方法按字母排序
res := val.Method(0).Call(params) //传入参数 reflect.Value
fmt.Println("res=",res[0].Int()) //返回结果 reflect.Value
}

func testMonster(){
monster := Monster{
Name: "tom",
Age: 18,
Score: 30,
Sex: "man",
}
TestStruct(monster)
}
  1. 适配器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func bridge(call interface{}, args ...interface{}){
n := len(args)
inValue := make([]reflect.Value, n)
for i := 0; i < n; i++ {
inValue[i] = reflect.ValueOf(args[i])
}
function := reflect.ValueOf(call)
function.Call(inValue)
}

func TestReflectFunc(){
call1 := func(v1 ,v2 int) {
fmt.Println(v1,v2)
}
call2 := func(v1 ,v2 int,s string) {
fmt.Println(v1,v2,s)
}
bridge(call1, 1,2)
bridge(call2, 1, 2, "s")
}

四、网络编程

Golang的主要设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端程序必不可少也是至关重要的一部分。

服务端的处理流程

  1. 监听端口

  2. 接收客户端的tcp链接,建立客户端和服务器端的链接.

  3. 创建goroutine,处理该链接的请求(通常客户端会通过链接发送请求包)

客户端的处理流程

  1. 建立与服务端的链接
  2. 发送请求数据,接收服务器端返回的结果数据
  3. 关闭链接

server.go

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
func process(conn net.Conn) {
//循环接收
defer conn.Close() //关闭
for {
//创建切片
buf := make([]byte, 1024)
//等等客户端发送数据,这里会一直阻塞
fmt.Printf("等等客户端数据:%v\n", conn.RemoteAddr())
n, err := conn.Read(buf) //读取数据
if err == io.EOF {
fmt.Println("客户端退出")
return
}
fmt.Println(string(buf[:n]))
}
}

func main(){
fmt.Println("服务器开始监听")
listen, err := net.Listen("tcp", "127.0.0.1:8888")
if err != nil {
fmt.Println("listen err=",err)
return
}
defer listen.Close()
fmt.Printf("listen suc=%v\n", listen)

//循环等待
for {
//等待客户端
fmt.Println("等待客户端")
conn, err := listen.Accept()
if err != nil {
fmt.Println("Accept err=",err)
}else {
fmt.Printf("Accept suc=%v,client ip=%v \n", conn, conn.RemoteAddr())
go process(conn)
}
}
}

client.go

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
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:8888")
if err != nil {
fmt.Println("client dial err=",err)
return
}
fmt.Printf("conn suc=%v\n", conn)
//发送数据
//从终端获取
reader := bufio.NewReader(os.Stdin)
for {
line, err := reader.ReadString('\n')
if err != nil {
fmt.Println("ReadString err=",err)
}
//exit退出
if strings.Trim(line, "\r\n") == "exit" {
fmt.Println("客户端退出")
break
}
//发送消息
n, err := conn.Write([]byte(line))
if err != nil {
fmt.Println("Write err=", err)
return
}
fmt.Printf("发送了 %d 字节的数据\n", n)
}
}