golang(7):文件读写 & json & 错误处理

时间:2023-07-29 23:01:58
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 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 >= '' && v <= '':
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: NumCount: SpaceCount: OtherCount:
[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,)
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()
}
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()
}
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, )    // 第一个参数是 文件名,第二参数是 模式, 第三个参数是 权限
// 第二个参数:文件打开模式
. os.O_WRONLY:只写
. os.O_CREATE:创建文件
. os.O_RDONLY:只读
. os.O_RDWR:读写
. os.O_TRUNC :清空
// 第三个参数:权限控制:
r ——>
w——>
x——>

示例代码:

// 示例代码:
package main import (
"fmt"
"bufio"
"os"
) func main() {
outputFile,outputError := os.OpenFile("output.txt",os.O_WRONLY|os.O_CREATE,) // 文件不存在则创建
if outputError != nil {
fmt.Println("an error occureed with file creation")
return
}
defer outputFile.Close() outputWriter := bufio.NewWriter(outputFile) // 带缓存区的写;传入文件句柄
for i := ; i < ; i++ {
outputWriter.WriteString("hello world\n") // 写入
}
outputWriter.Flush() // 由于是带缓存区的写,所以最后 Flush() 一下,把缓存区的内容从内存刷(写入)到磁盘里面
} // 运行结果:
[root@NEO example04_writeFile]# go run main/main.go
[root@NEO example04_writeFile]# ll
total
drwxr-xr-x root root Aug : main
-rw-r--r-- root root Aug : 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,)
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
drwxr-xr-x root root Aug : main
-rw-r--r-- root root Aug : output.txt
-rw-r--r-- root root Aug : 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:
os.Args[]=bin/example07_osArgs01
os.Args[]=-c
os.Args[]=/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", , "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",,"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: // -d 没指定就用默认的 0
[root@NEO project]#

Json数据协议

. 导入包:Import “encoding/json”
. 序列化: json.Marshal(data interface{}) ([]byte, error) // 返回值: 第一个是 字符数组,第二个错误
. 反序列化: 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:,
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 :=
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"] = 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"] =
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":,"Sex":"男","Email":"110@qq.com"} {"age":,"name":"neo"}
[{"id":,"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:,
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"] = 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 男 @qq.com} type:main.User
m:map[age: 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:-- ::22.276422373 + CST m=+0.000517782 msg:open nvosdanvdsoa.aaatxt: no such file or directory
get path error: path:nvosdanvdsoa.aaatxt op:read opTime:-- ::22.276422373 + 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")
}