一、文件 打开关闭 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 := 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" 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 := bufio.NewWriter(file) writer.WriteString(str) writer.Flush() }
文件打开方式
1 2 3 4 5 6 7 8 9 10 11 12 const ( O_RDONLY int = syscall.O_RDONLY O_WRONLY int = syscall.O_WRONLY O_RDWR int = syscall.O_RDWR O_APPEND int = syscall.O_APPEND O_CREATE int = syscall.O_CREAT O_EXCL int = syscall.O_EXCL O_SYNC int = syscall.O_SYNC O_TRUNC int = syscall.O_TRUNC )
判断文件是否存在 golang判断文件或文件夹是否存在的方法为使用os.stat()
函数返回的错误值进行判断:
如果返回的错误为nil,说明文件或文件夹存在
如果返回的错误类型使用os.IsNotExist()
判断为true,说明文件或文件夹不存在
如果返回的错误为其它类型,则不确定是否在存在
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 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) }
二、单元测试 Go语言中自带有一个轻量级的测试框架testing
和自带的go test
命令来实现单元测试和性能测试,testing框架和其他语言中的测试框架类似,可以基于这个框架写针对相应函数的测试用例 ,也可以基于该框架写相应的压力测试用例。通过单元测试,可以解决如下问题:
确保每个函数是可运行,并且运行结果是正确的
确保写出来的代码性能是好 的,
单元测试能及时的发现程序设计或实现的逻辑错误,使问题及早暴露,便于问题的定位解决,而性能测试的重点在于发现程序设计上的一些问题,让程序能够在高并发的情况下还能保持稳定
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√ } bridge(test1,1 ,2 ) bridge(test2,1 ,2 , "test2" )
反射可以在运行时动态获取变量的各种信息,比如变量的类型(type),类别(kind)
如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法 )
通过反射,可以修改变量的值,可以调用关联的方法。
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 stufunc test (b interface {}) { rVal := reflect.ValueOf(b) iVal := rVal.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 {}) { rT := reflect.TypeOf(b) fmt.Println("rType =" ,rT) rV := reflect.ValueOf(b) fmt.Println("rValue =" ,rV) 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 {}) { rT := reflect.TypeOf(b) fmt.Println("rType =" ,rT) rV := reflect.ValueOf(b) fmt.Println("rValue =" ,rV) 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()) rV.Elem().SetInt(20 ) }
反射实践
通过反射变量结构体的字段,调研结构体方法,并获得结构体标签的值
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) fmt.Println("res=" ,res[0 ].Int()) } func testMonster () { monster := Monster{ Name: "tom" , Age: 18 , Score: 30 , Sex: "man" , } TestStruct(monster) }
适配器
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的主要设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端程序必不可少也是至关重要的一部分。
服务端的处理流程
监听端口
接收客户端的tcp链接,建立客户端和服务器端的链接.
创建goroutine,处理该链接的请求(通常客户端会通过链接发送请求包)
客户端的处理流程
建立与服务端的链接
发送请求数据,接收服务器端返回的结果数据
关闭链接
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) } 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) } }