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!
", 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('
')        // 带缓存区的读取; ReadString() 中的参数是 byte 形式的 分隔符,所以要用 单引号
    if err != nil {
        fmt.Println("read string failed:",err)
        return
    }

    fmt.Printf("read string success,str: %s
",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('
')        // 以 
 作为分隔符,表示读取一行
    if err != nil {
        fmt.Println("read string failed,",err)
    }
    fmt.Printf("str:%s
",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('
')        // 以 
 作为分隔符表示每次读取一行
        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 == '	':
                    count.SpaceCount++
                default:
                    count.OtherCount++
            }
        }
    }
    fmt.Printf("ChCount:%d NumCount:%d SpaceCount:%d OtherCount:%d
",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
",string(buf))    // 把字节数组转化为字符串
    
    err = ioutil.WriteFile("/root/main_copy.go",buf,0666)
    if err != nil {
        fmt.Printf("write file err:%s
",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
",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
",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
",err)
        return 
    }

    reader := bufio.NewReader(fz)    // 让 fz 通过带缓存区的读
    for {
        line, err := reader.ReadString('
')
        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
")    // 写入
    }
    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
",len(os.Args))

    for i,v := range os.Args {
        fmt.Printf("os.Args[%d]=%s
",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
",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
",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
",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
",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
",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
",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
",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
",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
", e)
        }
    }()
    badCall()
    fmt.Printf("After bad call
")
}

func main() {
    fmt.Printf("Calling test
")
    test()
    fmt.Printf("Test completed
")
}
原文地址:https://www.cnblogs.com/neozheng/p/11297027.html