终端读写
操作终端相关文件句柄常量
os.Stdin // 标准输入 os.Stdout // 标准输出 (输出到终端) os.Stderr // 标准错误输出 (输出到终端)
fmt 常见用法
fmt.Scanln(&firstName, &lastName) // 从终端读取字符串(以空格分隔) fmt.Scanf("%s %s", &firstName, &lastName) // 作用同 fmt.Scanln(),但 fmt.Scanf() 既能读取字符串也能读取数字 fmt.Printf("Hi %s %s!\n", firstName, lastName) // 格式化输出 fmt.Sscanf(input, format, &f, &i, &s) // 从字符串中格式化读入(第一个参数表示要读取的字符串,第二个参数表示读取格式,后面的参数表示赋给哪些变量)
示例代码:
package main import "fmt" type Student struct{ Name string Age int Score float64 } func main(){ var s = "stu01 age is 18 score is 95.5" var stu Student fmt.Sscanf(s,"%s age is %d score is %f",&stu.Name,&stu.Age,&stu.Score) // 变量要传入地址 fmt.Println(stu) } // 运行结果: [root@NEO example01_Sscanf]# go run main/main.go {stu01 18 95.5} [root@NEO example01_Sscanf]#
带缓冲区的读写:
示例代码1:(从标准输入中读取)
package main import ( "bufio" "os" "fmt" ) func main(){ reader := bufio.NewReader(os.Stdin) // go 的 struct 没有构造函数,所以 bufio 包中自己写了一个构造函数 NewReader,回一个 Reader实现的指针;bufio.NewReader() 需要传入一个 io.Reader 接口,这个接口中吸有一个 Read() 方法,而 os.Stdin 也实现 Read() 方法,即实现了 io.Reader 这个接口 fmt.Println("pls input:") str,err := reader.ReadString('\n') // 带缓存区的读取; ReadString() 中的参数是 byte 形式的 分隔符,所以要用 单引号 if err != nil { fmt.Println("read string failed:",err) return } fmt.Printf("read string success,str: %s\n",str) } // 运行结果: [root@NEO example02_bufio01]# go run main/main.go pls input: i am neo read string success,str: i am neo [root@NEO example02_bufio01]#
示例代码2:(从文件中读取)
package main import ( "fmt" "os" "bufio" ) func main() { file,err := os.Open("/root/echotest.txt") // 读取文件用 os.Open(),写入文件用 os.OpenFile() if err != nil { fmt.Println("read file failed,",err) return } defer file.Close() // 关闭文件 reader := bufio.NewReader(file) // 把读取的对象 file 作为参数传到 bufio.NewReader() 中 str,err := reader.ReadString('\n') // 以 \n 作为分隔符,表示读取一行 if err != nil { fmt.Println("read string failed,",err) } fmt.Printf("str:%s\n",str) } // 运行结果: [root@NEO main]# go run main.go str:hello world neo [root@NEO main]#
练习,从终端读取一行字符串,统计英文、数字、空格以及其他字符的数量。
// 示例代码: package main import ( "fmt" "bufio" "os" "io" ) type CharCount struct { ChCount int NumCount int SpaceCount int OtherCount int } func main(){ file,err := os.Open("/root/go/project/src/go_dev/day07/example02_bufio03_count/main/main.go") if err != nil { fmt.Println("read file err:",err) return } defer file.Close() var count CharCount reader := bufio.NewReader(file) for { str,err := reader.ReadString('\n') // 以 \n 作为分隔符表示每次读取一行 if err == io.EOF { // io.EOF 表示文件读取完后返回的错误(表示文件已经读取完) break } if err != nil { fmt.Println("read string failed,",err) break } runeArr := []rune(str) // 把 str 字符串转化为 rune 切片 for _,v := range runeArr{ switch { case v >= 'a' && v <= 'z': fallthrough case v>= 'A' && v <= 'Z': count.ChCount++ case v >= '0' && v <= '9': count.NumCount++ case v == ' ' || v == '\t': count.SpaceCount++ default: count.OtherCount++ } } } fmt.Printf("ChCount:%d NumCount:%d SpaceCount:%d OtherCount:%d\n",count.ChCount,count.NumCount,count.SpaceCount,count.OtherCount) } // 运行结果: [root@NEO example02_bufio03_count]# go run main/main.go ChCount:598 NumCount:8 SpaceCount:186 OtherCount:290 [root@NEO example02_bufio03_count]# // EOF is the error returned by Read when no more input is available. io.EOF 是文件所有内容读取完后返回的错误 (表示已经把文件读取完了)
文件读写
os.File封装所有文件相关操作(os.File 是一个结构体), os.Stdin,os.Stdout, os.Stderr都是 *os.File 打开一个文件进行读操作: os.Open(name string) (*File, error) 关闭一个文件: File.Close()
读取整个文件示例
import "io/ioutil" buf, err := ioutil.ReadFile(filename string) // 读取整个文件; buf 是一个字节数组 err := ioutil.WriteFile(filename string, data []byte, perm os.FileMode) // 把整个字符串写入文件; perm os.FileMode 表示文件权限
示例代码:
package main import ( "io/ioutil" "fmt" "os" ) func main() { buf, err := ioutil.ReadFile("/root/main.go") // 读取整个文件;buf 是一个字节数组 if err != nil { fmt.Println(os.Stderr,"Read file err:",err) return } fmt.Printf("%s\n",string(buf)) // 把字节数组转化为字符串 err = ioutil.WriteFile("/root/main_copy.go",buf,0666) if err != nil { fmt.Printf("write file err:%s\n",err) } } // 运行结果: [root@NEO example02_bufio04_ioutil]# go run main/main.go package main import ( "fmt" ) func main(){ var a *int = new(int) var b interface{} b = a fmt.Printf("%v %T %v %T\n",b,b,*(b.(*int)),b.(*int)) } [root@NEO example02_bufio04_ioutil]#
读取压缩文件示例
// 示例代码: package main import ( "fmt" "os" "compress/gzip" // 解压缩文件的包 "bufio" ) func main(){ fName := "/root/echotest.tar.gz" file,err := os.Open(fName) // file 是文件句柄 if err != nil { fmt.Fprintf(os.Stderr,"Can not open %s, error: %s\n",fName,err) os.Exit(1) } defer file.Close() fz,err := gzip.NewReader(file) // 把文件句柄file 传入 gzip.NewReader();读一部分数据,然后再解压缩,再输出;fz 具有解压缩功能,是 *Reader if err != nil { fmt.Fprintf(os.Stderr,"open gzip failed,err:%v\n",err) return } reader := bufio.NewReader(fz) // 让 fz 通过带缓存区的读 for { line, err := reader.ReadString('\n') if err != nil { fmt.Println("reading file done") os.Exit(0) } fmt.Println(line) } } // 运行结果: [root@NEO example03_gzip]# go run main/main.go echotest.txt0000644000000000000000000000002013521457003012123 0ustar rootroothello world neo reading file done [root@NEO example03_gzip]#
文件写入
os.OpenFile(“output.dat”, os.O_WRONLY|os.O_CREATE, 0666) // 第一个参数是 文件名,第二参数是 模式, 第三个参数是 权限 // 第二个参数:文件打开模式 1. os.O_WRONLY:只写 2. os.O_CREATE:创建文件 3. os.O_RDONLY:只读 4. os.O_RDWR:读写 5. os.O_TRUNC :清空 // 第三个参数:权限控制: r ——> 004 w——> 002 x——> 001
示例代码:
// 示例代码: package main import ( "fmt" "bufio" "os" ) func main() { outputFile,outputError := os.OpenFile("output.txt",os.O_WRONLY|os.O_CREATE,0666) // 文件不存在则创建 if outputError != nil { fmt.Println("an error occureed with file creation") return } defer outputFile.Close() outputWriter := bufio.NewWriter(outputFile) // 带缓存区的写;传入文件句柄 for i := 0; i < 5; i++ { outputWriter.WriteString("hello world\n") // 写入 } outputWriter.Flush() // 由于是带缓存区的写,所以最后 Flush() 一下,把缓存区的内容从内存刷(写入)到磁盘里面 } // 运行结果: [root@NEO example04_writeFile]# go run main/main.go [root@NEO example04_writeFile]# ll total 8 drwxr-xr-x 2 root root 4096 Aug 7 01:19 main -rw-r--r-- 1 root root 60 Aug 7 01:19 output.txt [root@NEO example04_writeFile]# cat output.txt hello world hello world hello world hello world hello world [root@NEO example04_writeFile]#
拷贝文件
// 示例代码: package main import ( "os" "io" "fmt" ) func CopyFile(dstName,srcName string) (written int64, err error){ // 打开读文件(被拷贝的文件) 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() return io.Copy(dst,src) // io.Copy(目的文件句柄,源文件句柄) --> 拷贝文件 } func main(){ _,err := CopyFile("target.txt","output.txt") if err != nil { fmt.Println("copy file failed") return } fmt.Println("copy file done") } // 运行结果: [root@NEO example05_copyFile]# echo "copy file test" > output.txt [root@NEO example05_copyFile]# go run main/main.go copy file done [root@NEO example05_copyFile]# ll total 12 drwxr-xr-x 2 root root 4096 Aug 7 01:38 main -rw-r--r-- 1 root root 15 Aug 7 01:39 output.txt -rw-r--r-- 1 root root 15 Aug 7 01:39 target.txt [root@NEO example05_copyFile]#
命令行参数
os.Args是一个string的切片,用来存储所有的命令行参数 // os.Args 中的每个元素都是字符串;os.Args 的第一个元素是程序本身(路径+程序名) flag包的使用,用来解析命令行参数
原始方式读取命令行参数:os.Args
// 示例代码: package main import ( "fmt" "os" ) func main(){ fmt.Printf("len of os.Args:%d\n",len(os.Args)) for i,v := range os.Args { fmt.Printf("os.Args[%d]=%s\n",i,v) } } // 编译后的运行结果: [root@NEO project]# go build -o bin/example07_osArgs01 go_dev/day07/example07_osArgs01/main [root@NEO project]# bin/example07_osArgs01 -c /root/data/ len of os.Args:3 os.Args[0]=bin/example07_osArgs01 os.Args[1]=-c os.Args[2]=/root/data/ [root@NEO project]#
flag包的使用,用来解析命令行参数:
flag.BoolVar(&test, "b", false, "print on newline") // 第一个参数:用于接收值的一个变量地址;第二个参数表示 key 的名字;第三个参数是 默认值;第四个参数是 使用说明 flag.StringVar(&str, "s", "", "print on newline") flag.IntVar(&count, "c", 1001, "print on newline")
示例代码:
package main import ( "fmt" "flag" ) func main(){ var confPath string var logLevel int flag.StringVar(&confPath,"c","","pls input conf path") flag.IntVar(&logLevel,"d",0,"pls input log level") flag.Parse() // Parse() 之后 从命令行接收的参数都会生效 fmt.Println("path:",confPath) fmt.Println("log level:",logLevel) } // 编译后的运行结果: [root@NEO project]# go build -o bin/example07_osArgs02 go_dev/day07/example07_osArgs02/main [root@NEO project]# bin/example07_osArgs02 -c /root/data/test.conf // -c 指定 confPath path: /root/data/test.conf log level: 0 // -d 没指定就用默认的 0 [root@NEO project]#
Json数据协议
1. 导入包:Import “encoding/json” 2. 序列化: json.Marshal(data interface{}) ([]byte, error) // 返回值: 第一个是 字符数组,第二个错误 3. 反序列化: json.UnMarshal(data []byte, v interface{}) error // 返回值是一个错误 error
序列化示例:
// 示例代码: package main import ( "fmt" "encoding/json" ) type User struct{ UserName string // 需要json打包时,struct 中的字段首字母要大写 NickName string `json:"nickname"` // tag 可以改变 json打包时的 key Age int Sex string Email string } func jsonStruct(){ user := &User{ UserName:"user01", NickName:"ponyma", Age:56, Sex:"男", Email:"110@qq.com", } data,err := json.Marshal(user) if err != nil { fmt.Printf("json.Marshal failed,err:",err) return } fmt.Printf("%s\n",string(data)) // json.Marshal() 返回的第一个参数是 字符数组 []byte,利用 string()强转为 字符串 } func jsonInt(){ a := 10 data,err := json.Marshal(a) if err != nil { fmt.Printf("json.Marshal failed,err:",err) return } fmt.Printf("%s\n",string(data)) } func jsonMap(){ var m map[string]interface{} // map的value为空接口,可以接收任何类型 m = make(map[string]interface{}) // 给 map 赋值前要先初始化 m["name"] = "neo" m["age"] = 18 data,err := json.Marshal(m) if err != nil { fmt.Printf("json.Marshal failed,err:",err) return } fmt.Printf("%s\n",string(data)) } func jsonSlice(){ var slice []map[string]interface{} var m1 map[string]interface{} m1 = make(map[string]interface{}) m1["id"] = 1 m1["name"] = "neo01" slice = append(slice,m1) var m2 map[string]interface{} m2 = make(map[string]interface{}) m2["key1"] = "hello" m2["key2"] = "world" slice = append(slice,m2) data,err := json.Marshal(slice) if err != nil { fmt.Printf("json.Marshal failed,err:",err) return } fmt.Printf("%s\n",string(data)) } func main(){ jsonStruct() jsonInt() jsonMap() jsonSlice() } // 运行结果: [root@NEO example08_json01]# go run main/main.go {"UserName":"user01","nickname":"ponyma","Age":56,"Sex":"男","Email":"110@qq.com"} 10 {"age":18,"name":"neo"} [{"id":1,"name":"neo01"},{"key1":"hello","key2":"world"}] [root@NEO example08_json01]#
反序列化示例:
// 示例代码: package main import ( "fmt" "encoding/json" ) type User struct{ UserName string // 需要json打包时,struct 中的字段首字母要大写 NickName string `json:"nickname"` // tag 可以改变 json打包时的 key Age int Sex string Email string } func jsonStruct() (ret string, err error) { user := &User{ UserName:"user01", NickName:"ponyma", Age:56, Sex:"男", Email:"110@qq.com", } data,err := json.Marshal(user) // 序列化 if err != nil { fmt.Errorf("json.Marshal failed,err:%v\n",err) // fmt.Errorf() --> 错误格式化 return } ret = string(data) return } func unmarshalStruct(){ ret,err := jsonStruct() // ret 是 string 型 if err != nil { fmt.Println("json.Marshal failed,err:",err) return } var user User err = json.Unmarshal([]byte(ret), &user) // 反序列化;第一个参数:字节数组;第二个参数:空接口(可接收任何类型) if err != nil { fmt.Println("json.Unmarshal failed,err:",err) return } fmt.Printf("ret:%v type:%T\n",user,user) } func jsonMap() (ret string, err error){ var m map[string]interface{} m = make(map[string]interface{}) m["name"] = "neo" m["age"] = 18 data,err := json.Marshal(m) if err != nil { fmt.Errorf("json.Marshal failed,err:%v\n",err) return } ret = string(data) return } func unmarshalMap(){ ret,err := jsonMap() // ret 是 string 型 if err != nil { fmt.Println("json.marshal failed,err:",err) return } var m map[string]interface{} err = json.Unmarshal([]byte(ret),&m) // 要改变指向 m 这个 map 指针的指针 if err != nil { fmt.Println("json.unmarshal failed,err:",err) return } fmt.Printf("m:%v type:%T\n",m,m) } func main(){ unmarshalStruct() unmarshalMap() } // 运行结果: [root@NEO example08_json02_unmarshal]# go run main/main.go ret:{user01 ponyma 56 男 110@qq.com} type:main.User m:map[age:18 name:neo] type:map[string]interface {} [root@NEO example08_json02_unmarshal]#
错误处理
定义错误
// 示例代码: package main import ( "errors" "fmt" ) var errNotFound error = errors.New("Not found error") // 自定义错误信息; errNotFound 是 string (用系统自带的 errors.New() 就可以满足大部分需求) func main() { fmt.Printf("error: %v", errNotFound) }
自定义错误:
// 我们每次返回收到的 error 是一个接口,如下: type error interface { Error() string }
示例代码:
// 示例代码1: package main import ( // "fmt" ) type PathError struct { // 自定义一个错误类型;其实现了 error 接口,所以就可以通过 error 去返回 Op string Path string err string } func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() } func test() error { return &PathError{ Op: "op", Path: "path", } } func main() { test() } // 示例代码2: package main import ( "fmt" "os" "time" ) type PathError struct { // PathError 是一个自定义错误结构体;PathError 实现了 error 接口,所以 PathError 就可以通过 error 去返回 path string op string opTime string msg string } func (p *PathError) Error() string{ // 实现 error 接口 return fmt.Sprintf("path:%s op:%s opTime:%s msg:%s",p.path,p.op,p.opTime,p.msg) } func Open(filename string) error { // 通过 error 去返回 file,err := os.Open(filename) if err != nil { return &PathError{ // 通过 error 去返回 path:filename, op:"read", opTime:fmt.Sprintf("%v",time.Now()), msg:err.Error(), } } defer file.Close() return nil // nil 表示没有错误 } func main() { err := Open("nvosdanvdsoa.aaatxt") // 返回的是我们自定义的错误 if err != nil { fmt.Println(err) v,ok := err.(*PathError) // 判断是不是我们自定义的错误类型 if ok { fmt.Println("get path error:",v) } } } // 运行结果: [root@NEO example09_error01]# go run main/main.go path:nvosdanvdsoa.aaatxt op:read opTime:2019-08-08 01:08:22.276422373 +0800 CST m=+0.000517782 msg:open nvosdanvdsoa.aaatxt: no such file or directory get path error: path:nvosdanvdsoa.aaatxt op:read opTime:2019-08-08 01:08:22.276422373 +0800 CST m=+0.000517782 msg:open nvosdanvdsoa.aaatxt: no such file or directory [root@NEO example09_error01]# // 判断自定义错误的另一种方法: switch err := err.(type) { case ParseError: PrintParseError(err) case PathError: PrintPathError(err) ... default: }
Panic & Recover
// 示例代码: package main import ( "fmt" ) func badCall() { panic("bad end") // 让程序 panic } func test() { defer func() { if e := recover(); e != nil { // 用 recover() 去捕获错误 fmt.Printf("Panicking %s\r\n", e) } }() badCall() fmt.Printf("After bad call\r\n") } func main() { fmt.Printf("Calling test\r\n") test() fmt.Printf("Test completed\r\n") }