golang(7):文件讀寫 & json & 錯誤處理


終端讀寫

操作終端相關文件句柄常量

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 56110@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")
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM