本节主要内容:
1. 终端读写
2. 文件读写
3. 命令行参数
4. Json
5. 自定义错误
6. 课后作业
1. 终端读写
操作终端相关文件句柄常量
os.Stdin:标准输入
os.Stdout:标准输出
os.Stderr:标准错误输出
1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 var ( 8 firstName, lastName, s string 9 i int 10 f float32 11 input = "56.12 / 5212 / Go" 12 format = "%f / %d / %s" 13 ) 14 15 func main() { 16 fmt.Println("Please enter your full name: ") //Chris Naegels 17 fmt.Scanln(&firstName, &lastName) //和下面等价 18 // fmt.Scanf("%s %s", &firstName, &lastName) 19 fmt.Printf("Hi %s %s!\n", firstName, lastName) // Hi Chris Naegels! 20 fmt.Sscanf(input, format, &f, &i, &s) 21 fmt.Println("From the string we read: ", f, i, s) //From the string we read: 56.12 5212 Go 22 }
1 package main 2 3 import ( 4 "bufio" 5 "fmt" 6 "os" 7 ) 8 9 func main() { 10 fmt.Println("Input string >>") 11 reader := bufio.NewReader(os.Stdin) 12 res, err := reader.ReadString('\n') 13 if err != nil { 14 fmt.Println("Read failed, Error: ", err) 15 } 16 fmt.Printf("Read success, content: ", res) 17 }
1 package main 2 3 import ( 4 "fmt" 5 "os" 6 "bufio" 7 ) 8 9 func main() { 10 // fmt.Fprintf(os.Stdout, "%s\n", "hello world") 11 12 buf := bufio.NewWriter(os.Stdout) 13 fmt.Fprintf(buf, "%s\n", "hello world") 14 buf.Flush() 15 }
2. 文件读写
(1)文件读取
bufio模块通过对io模块的封装,提供了数据缓冲功能,能够一定程度减少大块数据读写带来的开销。
实际上在bufio各个组件内部都维护了一个缓冲区,数据读写操作都直接通过缓存区进行。当发起一次读写操作时,会首先尝试从缓冲区获取数据;只有当缓冲区没有数据 时,才会从数据源获取数据更新缓冲。
1 package main 2 3 import ( 4 "os" 5 "fmt" 6 "bufio" 7 ) 8 9 func main() { 10 var input *bufio.Reader 11 var str string 12 var err error 13 14 input = bufio.NewReader(os.Stdin) 15 str, err = input.ReadString('\n') //注意:ReadString会返回读取的字符串(包括分隔符'\n') 16 if err != nil { 17 fmt.Println("Read failed") 18 } 19 fmt.Println("Read success, content: ", str) 20 }
练习: 从终端读取一行字符串,统计英文、数字、空格以及其他字符的数量。
1 package main 2 3 import ( 4 "fmt" 5 "os" 6 "bufio" 7 "strings" 8 ) 9 10 type CharCount struct { 11 ChCount int 12 NumCount int 13 SpaceCount int 14 OtherCount int 15 } 16 17 func Count(str string, cc *CharCount) { 18 runeArr := []rune(str) 19 for _, v := range runeArr { 20 fmt.Printf("--%v\n",v) 21 switch { 22 case v >= 'A' && v <= 'Z': 23 fallthrough 24 case v >= 'a' && v <= 'z': 25 cc.ChCount++ 26 case v >= '0' && v <= '9': 27 cc.NumCount++ 28 case v == ' ': 29 cc.SpaceCount++ 30 default: 31 cc.OtherCount++ 32 } 33 } 34 } 35 36 func main() { 37 var cc CharCount 38 39 fmt.Println("Input string >>") 40 reader := bufio.NewReader(os.Stdin) 41 str, err := reader.ReadString('\n') 42 if err != nil { 43 fmt.Println("Read failed") 44 } 45 46 str = strings.Trim(string(str), "\r\n") //去掉末尾的\r\n,否则OtherCount会多加2 47 48 Count(string(str), &cc) 49 fmt.Println(cc) 50 }
os.File封装所有文件相关操作,之前讲的 os.Stdin,os.Stdout, os.Stderr都是*os.File
a. 打开一个文件进行读操作: os.Open(name string) (*File, error)
b. 关闭一个文件:File.Close()
1)将整个文件读取到内存
1 package main 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 ) 7 8 func main() { 9 fileName := "F:\\Go\\project\\src\\go_dev\\day7\\test.txt" 10 data, err := ioutil.ReadFile(fileName) 11 if err != nil { 12 fmt.Println("File reading error", err) 13 return 14 } 15 fmt.Println("Contents of file: ", string(data)) 16 }
2)分块读取文件
在前面的章节,我们学习了如何把整个文件读取到内存。当文件非常大时,尤其在 RAM 存储量不足的情况下,把整个文件都读入内存是没有意义的。更好的方法是分块读取文件。这可以使用 bufio 包来完成。
1 package main 2 3 import ( 4 "bufio" 5 "fmt" 6 "log" 7 "os" 8 "io" 9 ) 10 11 func EmptyArray(arr []byte) { 12 for i := 0; i < len(arr); i++ { 13 arr[i] = 0 14 } 15 } 16 17 func main() { 18 fileName := "F:\\Go\\project\\src\\go_dev\\day7\\test.txt" 19 20 f, err := os.Open(fileName) 21 if err != nil { 22 log.Fatal(err) 23 } 24 25 defer func() { 26 if err = f.Close(); err != nil { 27 log.Fatal(err) 28 } 29 }() 30 31 var str string 32 r := bufio.NewReader(f) 33 b := make([]byte, 3) //每次读取3个字节 34 35 for { 36 EmptyArray(b) 37 38 _, err := r.Read(b) 39 if err == io.EOF { 40 fmt.Println("Read finish") 41 break 42 } 43 44 if err != nil { 45 fmt.Println("Error reading file: ", err) 46 break 47 } 48 str = string(b) 49 fmt.Printf("%s", str) 50 } 51 }
3)逐行读取文件
1 package main 2 3 import ( 4 "bufio" 5 "fmt" 6 "log" 7 "os" 8 ) 9 10 func main() { 11 fileName := "F:\\Go\\project\\src\\go_dev\\day7\\test.txt" 12 13 f, err := os.Open(fileName) 14 if err != nil { 15 log.Fatal(err) 16 } 17 18 defer func() { 19 if err = f.Close(); err != nil { 20 log.Fatal(err) 21 } 22 }() 23 24 s := bufio.NewScanner(f) 25 for s.Scan() { 26 fmt.Println(s.Text()) 27 } 28 29 if err = s.Err(); err != nil { 30 log.Fatal(err) 31 } 32 }
练习:读取压缩文件(zip)
1 package main 2 3 // 引入所需包 4 import ( 5 "os" 6 "compress/gzip" 7 "io/ioutil" 8 "fmt" 9 ) 10 11 // gzip文件 12 13 14 func main() { 15 fileName := "F:\\Go\\project\\src\\go_dev\\day7\\test.zip" 16 // 打开本地gz格式压缩包 17 fr, err := os.Open(fileName) 18 if err != nil { 19 panic(err) 20 } else { 21 println("open file success!") 22 } 23 24 // defer: 在函数退出时,执行关闭文件 25 defer fr.Close() 26 27 // 创建gzip文件读取对象 28 gr, err := gzip.NewReader(fr) 29 if err != nil { 30 panic(err) 31 } 32 33 // defer: 在函数退出时,执行关闭gzip对象 34 defer gr.Close() 35 36 // 读取gzip对象内容 37 rBuf, err := ioutil.ReadAll(gr) 38 if err != nil { 39 fmt.Println("[read gzip data err]: ", err) 40 } 41 42 // 以文本形式输出 43 fmt.Printf("%s\n", rBuf) 44 }
(2)文件写入
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
1 package main 2 3 import ( 4 "fmt" 5 "os" 6 "bufio" 7 ) 8 9 func main() { 10 filePath := "F:\\Go\\project\\src\\go_dev\\day7\\write_test.txt" 11 outFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666) 12 if err != nil { 13 fmt.Println("Failed to write file") 14 return 15 } 16 defer outFile.Close() 17 18 outWrite := bufio.NewWriter(outFile) 19 des := "hello world!\n" 20 for i := 0; i < 10; i++ { 21 outWrite.WriteString(des) 22 } 23 outWrite.Flush() 24 }
(3)拷贝文件
1 package main 2 3 import ( 4 "fmt" 5 "os" 6 "io" 7 ) 8 9 //A successful Copy returns err == nil, not err == EOF 10 func CopyFile(destName, srcName string) (written int64, err error) { 11 src, err := os.Open(srcName) 12 if err != nil { 13 panic(err) 14 } 15 defer src.Close() 16 17 dest, err := os.OpenFile(destName, os.O_WRONLY|os.O_CREATE, 0666) 18 if err != nil { 19 panic(err) 20 } 21 defer dest.Close() 22 23 return io.Copy(dest, src) 24 } 25 26 func main() { 27 srcName := "F:\\Go\\project\\src\\go_dev\\day7\\write_test.txt" 28 destName := "F:\\Go\\project\\src\\go_dev\\day7\\dest_test.txt" 29 30 wByte, err := CopyFile(destName, srcName) 31 if err != nil { 32 fmt.Println("Copy failed") 33 } else { 34 fmt.Printf("Copy %d byte from src to dest\n", wByte) 35 } 36 }
3. 命令行参数
os.Args是一个string的切片,用来存储所有的命令行参数
flag包的使用,用来解析命令行参数:
flag.BoolVar(&test, "b", false, "print on newline") flag.StringVar(&str, "s", "", "print on newline") flag.IntVar(&count, "c", 1001, "print on newline")
1 package main 2 3 import ( 4 "fmt" 5 "os" 6 ) 7 8 func main() { 9 fmt.Printf("len of args:%d\n", len(os.Args)) 10 for i, v := range os.Args { 11 fmt.Printf("args[%d]=%s\n", i, v) 12 } 13 } 14 15 //输入: 16 //go run main7.go hello world 17 // 输出: 18 // len of args:3 19 // args[0]=C:\Users\ADMINI~1\AppData\Local\Temp\go-build596136718\command-line-argu 20 // ments\_obj\exe\main7.exe 21 // args[1]=hello 22 // args[2]=world
1 package main 2 3 import ( 4 "fmt" 5 "flag" 6 ) 7 8 func main() { 9 var configPath string 10 var logLevel int 11 12 flag.StringVar(&configPath, "c", "", "Please config path") 13 flag.IntVar(&logLevel, "d", 10, "Please log level") 14 15 flag.Parse() 16 17 fmt.Println("configPath: ", configPath) 18 fmt.Println("logLevel: ", logLevel) 19 }
4. Json数据协议
func Marshal(v interface{}) ([]byte, error) func Unmarshal(data []byte, v interface{}) error
导入包:import "encoding/json" 序列化: json.Marshal(data interface{}) 反序列化: json.UnMarshal(data []byte, v interface{})
练习:json序列化结构体
1 package main 2 3 import ( 4 "encoding/json" 5 "fmt" 6 ) 7 8 type User struct { 9 UserName string `json:"username"` 10 NickName string `json:"nickname"` 11 Age int 12 Birthday string 13 Sex string 14 Email string 15 Phone string 16 } 17 18 func testStruct() { 19 user1 := &User{ 20 UserName: "user1", 21 NickName: "上课看似", 22 Age: 18, 23 Birthday: "2008/8/8", 24 Sex: "男", 25 Email: "[email protected]", 26 Phone: "110", 27 } 28 29 data, err := json.Marshal(user1) 30 if err != nil { 31 fmt.Printf("json.marshal failed, err:", err) 32 return 33 } 34 35 fmt.Printf("%s\n", string(data)) 36 } 37 38 func testInt() { 39 var age = 100 40 data, err := json.Marshal(age) 41 if err != nil { 42 fmt.Printf("json.marshal failed, err:", err) 43 return 44 } 45 46 fmt.Printf("%s\n", string(data)) 47 } 48 49 func testMap() { 50 var m map[string]interface{} 51 m = make(map[string]interface{}) 52 m["username"] = "user1" 53 m["age"] = 18 54 m["sex"] = "man" 55 56 data, err := json.Marshal(m) 57 if err != nil { 58 fmt.Printf("json.marshal failed, err:", err) 59 return 60 } 61 62 fmt.Printf("%s\n", string(data)) 63 } 64 65 func testSlice() { 66 var m map[string]interface{} 67 var s []map[string]interface{} 68 m = make(map[string]interface{}) 69 m["username"] = "user1" 70 m["age"] = 18 71 m["sex"] = "man" 72 73 s = append(s, m) 74 75 m = make(map[string]interface{}) 76 m["username"] = "user2" 77 m["age"] = 29 78 m["sex"] = "female" 79 s = append(s, m) 80 81 data, err := json.Marshal(s) 82 if err != nil { 83 fmt.Printf("json.marshal failed, err:", err) 84 return 85 } 86 87 //[{"age":18,"sex":"man","username":"user1"},{"age":29,"sex":"female","username":"user2"}] 88 fmt.Printf("%s\n", string(data)) 89 } 90 91 func main() { 92 //testStruct() 93 //testInt() 94 //testMap() 95 testSlice() 96 }
练习:json序列化map
1 package main 2 3 import ( 4 "encoding/json" 5 "fmt" 6 ) 7 8 type User struct { 9 UserName string `json:"username"` 10 NickName string `json:"nickname"` 11 Age int 12 Birthday string 13 Sex string 14 Email string 15 Phone string 16 } 17 18 func testStruct() (ret string, err error) { 19 user1 := &User{ 20 UserName: "user1", 21 NickName: "上课看似", 22 Age: 18, 23 Birthday: "2008/8/8", 24 Sex: "男", 25 Email: "[email protected]", 26 Phone: "110", 27 } 28 29 data, err := json.Marshal(user1) 30 if err != nil { 31 err = fmt.Errorf("json.marshal failed, err:", err) 32 return 33 } 34 35 ret = string(data) 36 return 37 } 38 39 func testMap() (ret string, err error) { 40 var m map[string]interface{} 41 m = make(map[string]interface{}) 42 m["username"] = "user1" 43 m["age"] = 18 44 m["sex"] = "man" 45 46 data, err := json.Marshal(m) 47 if err != nil { 48 err = fmt.Errorf("json.marshal failed, err:", err) 49 return 50 } 51 52 ret = string(data) 53 return 54 } 55 56 func test2() { 57 data, err := testMap() 58 if err != nil { 59 fmt.Println("test map failed, ", err) 60 return 61 } 62 63 var m map[string]interface{} 64 err = json.Unmarshal([]byte(data), &m) 65 if err != nil { 66 fmt.Println("Unmarshal failed, ", err) 67 return 68 } 69 fmt.Println(m) 70 } 71 72 func test() { 73 data, err := testStruct() 74 if err != nil { 75 fmt.Println("test struct failed, ", err) 76 return 77 } 78 79 var user1 User 80 err = json.Unmarshal([]byte(data), &user1) 81 if err != nil { 82 fmt.Println("Unmarshal failed, ", err) 83 return 84 } 85 fmt.Println(user1) 86 } 87 88 func main() { 89 test() 90 test2() 91 }
5. 自定义错误
type error interface { Error() string }
1 package main 2 3 import ( 4 "errors" 5 "fmt" 6 ) 7 8 var errNotFound error = errors.New("Not found error") 9 10 func main() { 11 fmt.Printf("error: %v", errNotFound) 12 }
1 package main 2 import ( 3 "fmt" 4 ) 5 type PathError struct { 6 Op string 7 Path string 8 Err string 9 } 10 11 func (e *PathError) Error() string { 12 return e.Op + " " + e.Path + ": " + e.Err 13 } 14 15 func test() error { 16 return &PathError{ 17 Op: "op", 18 Path: "path", 19 Err: "err", 20 } 21 } 22 func main() { 23 if err := test(); err != nil { 24 fmt.Println(err) //op path: err 25 } 26 }
如何判断自定义错误?
switch err := err.(type) { case ParseError: PrintParseError(err) case PathError: PrintPathError(err) default:
... }
1 package main 2 3 import ( 4 "fmt" 5 "os" 6 "time" 7 ) 8 9 type PathError struct { 10 path string 11 op string 12 createTime string 13 message string 14 } 15 16 func (p *PathError) Error() string { 17 return fmt.Sprintf("path=%s op=%s createTime=%s message=%s", p.path, 18 p.op, p.createTime, p.message) 19 } 20 21 func Open(filename string) error { 22 file, err := os.Open(filename) 23 if err != nil { 24 return &PathError{ 25 path: filename, 26 op: "read", 27 message: err.Error(), //注意这块的Error是系统的error定义的接口Error() 28 createTime: fmt.Sprintf("%v", time.Now()), 29 } 30 } 31 32 defer file.Close() 33 return nil 34 } 35 36 func main() { 37 err := Open("C:/sdklflakfljdsafjs.txt") 38 switch v := err.(type) { 39 case *PathError: 40 fmt.Println("get path error,", v) //v.Error() 类似于打印fmt.Println(err),其实内部实现fmt.Println(err.Error()) 41 default: 42 43 } 44 45 }
Panic&Recover
1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 func badCall() { 8 panic("bad end") 9 } 10 11 func test() { 12 defer func() { 13 if e := recover(); e != nil { //在这块通过recover捕获panic错误并处理 14 fmt.Printf("Panicking %s\r\n", e) 15 } 16 }() 17 18 badCall() 19 fmt.Printf("After bad call\r\n") 20 } 21 22 func main() { 23 fmt.Printf("Calling test\r\n") 24 test() 25 fmt.Printf("Test completed\r\n") 26 }
课后工作
实现一个图书管理系统v3,具有以下功能:
a. 增加持久化存储的功能
b. 增加日志记录的功能
参考文献:
https://studygolang.com/articles/14669