終端讀寫
操作終端相關文件句柄常量
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") }