GO语言学习之文件操作
1.文件的基本介绍
》文件的概念
文件,对我们并不陌生,文件是数据源(保存数据的地方)的一种,
文件最主要的作用就是保存数据,它既可以保存文字,图片,视频,声音
》输入流和输出流
文件在程序中是以流的形式来操作的
流:数据在数据源(文件)和程序(内存)之间经历的路径
输入流:数据从数据源(文件)到程序(内存)的路径
输出流:数据从程序(内存)到数据源(文件)的路径
》os.File封装所有文件相关操作,File是一个结构体
https://golang.org/src/os/types.go?s=369:411#L6
2.打开文件和关闭文件
》使用的函数和方法
Func Open
https://golang.org/src/os/file.go?s=9082:9148#L288
Open打开一个文件用于读取,如果操作成功,返回的文件对象的方法可用于读取数据;对应的文件描述符具有 O_RDONLY模式。如果出错,错误底层类型是 *PathError
func(*File)Close error
Close 关闭文件f,使文件不能用于读写。它返回可能出现的错误
》案例演示
package main
import (
"fmt"
"os"
)
func main() {
//打开文件
//概念说明:file的叫法
//1.file叫file对象
//2.file叫file指针
//3.file叫file 文件句柄
file ,err:=os.Open("D:/111.txt")
if err!=nil{
fmt.Println("open file err=",err)
}
//输出下文件,看看文件是什么,看出file就是一个指针 * *file
fmt.Printf("file=%v",file)//file=&{0xc00007a500}
//关闭文件 ,使文件不能用于读写,返回的是可能出现的错误
err=file.Close()
if err!=nil{
fmt.Println("close file err=",err)
}
}
3.读文件操作应用实例
1》读取文件的内容并显示在终端(带缓冲区的方式),使用os.Open,
file.Close,bufio.NewReader(),reader.ReadString的函数和方法
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
//打开文件
//概念说明:file的叫法
//1.file叫file对象
//2.file叫file指针
//3.file叫file 文件句柄
file ,err:=os.Open("D:/111.txt")
if err!=nil{
fmt.Println("open file err=",err)
}
//输出下文件,看看文件是什么,看出file就是一个指针 * *file
fmt.Printf("file=%v",file)//file=&{0xc00007a500}
fmt.Println()
//关闭文件 ,使文件不能用于读写,返回的是可能出现的错误
//当函数退出时,要及时关闭file句柄,否则会有内存泄漏、
defer file.Close()
//创建一个 *Reader,是带缓冲的
/*
const{
defaultBufSize=4096//默认的缓冲区大小为 4096
}
*/
reader:=bufio.NewReader(file)
//循环的读取文件的内容
for{
str,err:=reader.ReadString('\n')//读到一个换行就结束
if err==io.EOF{//io.EOF表示文件的结尾
break
}
fmt.Println(str)
}
fmt.Println("文件读完了")
}
2》读取文件的内容并显示在终端(使用ioutil 一次将整个文件读入到内存中),这种方式适用于文件不大的情况。相关方法和函数(ioutil.ReadFile)
代码演示:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
//使用 ioutil.ReadFile一次性将文件读取到位
file:="d:/111.txt"
fileContent,err:=ioutil.ReadFile(file)
if err!=nil{
fmt.Println("read file err=",err)
}
//把读取到的内容显示到 终端
fmt.Printf("%v",fileContent)//[]byte
fmt.Println()
fmt.Printf("%v",string(fileContent))
//上面代码我们米有显示的open文件,因此也不需要显示的close文件
//因为,文件的Open和cLOSE被封装到了 ReadFile函数的内部了
}
4.写文件操作应用实例
4.1基本介绍 -os.OpenFile函数
func OpenFile(name string, flag int, perm FileMode) (*File, error)
说明:os.OpenFile是一个更一般性的文件打开函数,它会使用指定的选项。如( O_WRONLY),指定的模式 如(0666)打开指定的名称的文件
如果操作成功,返回文件对象可用于i/o,如果出错,错误底层类型是 *PathError
第二个参数:flag int
// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
O_RDONLY int = syscall.O_RDONLY // open the file read-only.只读模式
O_WRONLY int = syscall.O_WRONLY // open the file write-only.只写模式
O_RDWR int = syscall.O_RDWR // open the file read-write.读写模式打开文件
// The remaining values may be or'ed in to control behavior.
O_APPEND int = syscall.O_APPEND // append data to the file when writing.写操作时将数据追加到文件尾部
O_CREATE int = syscall.O_CREAT // create a new file if none exists.如果不存在将创建一个文件
O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist.和 O_CREATE配合使用,文件必须不存在
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.打开文件用于同步I/O
O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened.
如果可能打开时清空文件
)
第三个参数:perm FileMode权限控制
r—>4
w—>2
x–>1
4.2基本应用实例(1)
1》创建一个 新文件,写入内容
代码实现:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
//创建一个 新文件,写入内容 5句话
//打开文件 d:/111.txt
filePath:="d:/111.txt"
file,err:=os.OpenFile(filePath,os.O_WRONLY|os.O_CREATE,0666)
if err!=nil{
fmt.Println("open file err=",err)
}
//及时关闭file 句柄
defer file.Close()
//准备写入 i love liuyifei
str:="i love liuyifei \n"//\n表示换行
//写入时,使用 带缓存的 *Writer
writer:=bufio.NewWriter(file)
for i:=0;i<5;i++{
writer.WriteString(str)
}
//因为 writer是带缓存的,因此在调用writerstring方法时,其实
//内容先写入到缓存中,所以需要调用 flush方法,将缓冲的数据,
//真正写入到文件中国,否则文件中没有数据
writer.Flush()
}
2》打开一个存在的文件,将原来的内容覆盖成新的内容
10句,hello golang
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
//打开一个存在的文件中,将原来的内容覆盖成新的内容
//打开已经存在的文件 "d:/111.txt"
filePath:="d:/111.txt"
file,err:=os.OpenFile(filePath,os.O_WRONLY|os.O_TRUNC,0666)
if err!=nil{
fmt.Printf("open file err=%v\n",err)
return
}
//及时关闭file句柄
defer file.Close()
//准备写入10句·”hello,world"
str:="hello,world\r\n"//表示换行
//写入时,使用带缓存的 *wRITER
writer:=bufio.NewWriter(file)
for i:=0;i<10;i++{
writer.WriteString(str)
}
//因为 writer是带缓存的,因此需要flush,将缓冲数据写入到文件中
writer.Flush()
}
3》打开一个存在的文件,在原来内容追加“hello 2020"
代码实现:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
//打开一个存在的文件,追加 hello 2020
//打开已经存在的文件 "d:/111.txt"
filePath:="d:/111.txt"
file,err:=os.OpenFile(filePath,os.O_WRONLY|os.O_APPEND,0666)
if err!=nil{
fmt.Println("file operate err=",err)
return
}
//及时关闭句柄
defer file.Close()
//写入十句 hello 2020
str:="hello 2020 \r\n"
//写入时,使用带 缓存的 writer
writer:=bufio.NewWriter(file)
for i:=0;i<10;i++{
writer.WriteString(str)
}
//使用缓冲将数据刷进文件
writer.Flush()
}
4》打开一个存在的文件,将原来的内容读出显示在终端,并且追加5句
你好,中关村
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
/*
打开一个存在的文件,将原来的内容读出显示在终端,并且追加5句
你好,中关村
*/
//打开已经存在的文件
filePath:="d:/111.txt"
file,err:=os.OpenFile(filePath,os.O_WRONLY|os.O_APPEND,0666)
if err!=nil{
fmt.Printf("open file err=%v\n",err)
return
}
//及时关闭file句柄
defer file.Close()
//先读取源文件内容,并显示在控制台
reader:=bufio.NewReader(file)
for{
str,err:=reader.ReadString('\n')
if err==io.EOF{
//
break
}
fmt.Print(str)
}
//写入十句 hello 2020
str:="你好中关村\r\n"
//写入时,使用带 缓存的 writer
writer:=bufio.NewWriter(file)
for i:=0;i<10;i++{
writer.WriteString(str)
}
//使用缓冲将数据刷进文件
writer.Flush()
}
4.3基本应用实例(2)
编程一个程序,将一个文件的内容,写入到另外一个文件,注意:这两个文件已经存在
说明:使用 ioutil.ReadFile/ioutil.WriterFile 完成
代码实现:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
//将 d:/111.txt 写入到 d:/222.txt
//首先将 111.txt内容读取到内存中
//再将读取到的内容写入到 222.txt中
file1Path:="d:/111.txt"
file2Path:="d:/222.txt"
fileContent,err:=ioutil.ReadFile(file1Path)
if err!=nil{
fmt.Println("file raader err=",err)
}
err=ioutil.WriteFile(file2Path,fileContent,0666)
if err!=nil{
fmt.Println("file writer err=",err)
}
}
4.4判断文件是否存在
golang判断文件或文件夹是否存在的方法为使用 os.Stat()函数返回的错误值进行判断
1》如果返回的错误为 nil,说明文件或文件夹存在
2》如果返回的错误类型使用 os.IsNotExist(),判断为 true,说明文件或者文件夹不存在
3》如果返回的错误为其他类型,则不确定是否存在不存在
func PathExists(path string)(bool,error){
_,err:=os.Stat(path)
if err==nil{
//文件或者目录存在
return true,nil
}
if os.IsNotExist(err){
return false,nil
}
return false,err
}
5.文件编程应用实例
5.1拷贝文件
说明:将一张图片/电影/mp3拷贝到另外一个文件
func Copy(dst Writer,src Reader)(written int64,err error)
注意:Copy函数是 io 包提供的
代码实现:
package main
import (
"bufio"
"fmt"
"io"
"os"
)
//自己编写一个函数,接收两个文件路径 srcFilePath,dstFilePath
func CopyFile(dstFilePath string,srcFilePath string)(written int64,err error){
srcFile,err:=os.Open(srcFilePath)
if err!=nil{
fmt.Println("open file err=",err)
}
defer srcFile.Close()
//通过 srcFile 获取到 reader
reader:=bufio.NewReader(srcFile)
//打开 dstFilePath
dstFile,err:=os.OpenFile(dstFilePath,os.O_WRONLY|os.O_CREATE,0666)
if err!=nil{
fmt.Println("open file err=",err)
return
}
//通过 dstFile ,获取到 writer
writer:=bufio.NewWriter(dstFile)
defer dstFile.Close()
//
return io.Copy(writer,reader)
}
func main() {
//将 d:/x.bmp 拷贝到 d:/y.bmp
srcFilePath:="d:/x.bmp"
dstFilePath:="d:/y.bmp"
_,err:=CopyFile(dstFilePath,srcFilePath)
if err==nil{
fmt.Println("拷贝完成")
}else {
fmt.Println("拷贝异常")
}
}
5.2统计英文,数字,空格和其他字符数量
说明:统计一个文件中含有的英文,数字,空格以及其他字符数量
代码实现:
package main
import (
"bufio"
"fmt"
"io"
"os"
)
//定义一个结构体,用于保存统计结果
type CharCount struct {
EngCount int //英文个数
NumCount int //记录字母个数
SpaceCount int //记录空格个数
OtherCount int//记录其他字符的个数
}
func main() {
//思路:打开一个文件,创建一个 reader
//每读取一行,就去统计改行有多少个字符
//然后将结果保存到结构体中
filePath:="d:/111.txt"
file,err:=os.Open(filePath)
if err!=nil{
fmt.Printf("open file err=%v \n",err)
return
}
defer file.Close()
//定义一个 CharCount 实例
var cc CharCount
//创建一个 reader
reader:=bufio.NewReader(file)
//开始循环读取 file中的内容
for{
str,err:=reader.ReadString('\n')
if err==io.EOF{
break
}
//为了兼容中文字符,将 str 转换成 []rune
str= string([]rune(str))
//遍历 str 进行统计
for _,v:=range str{
switch {
case v>='a'&&v<='z':
fallthrough
case v>='A'&&v<='Z' :
cc.EngCount++
case v==' '||v=='\t':
cc.SpaceCount++
case v>=0&&v<=9:
cc.NumCount++
default:
cc.OtherCount++
}
}
}
//输出统计结果
fmt.Printf("字母个数:%v \n数字个数:%v \n 空格个数为:%v \n 其他个数: %v \n",cc.EngCount,cc.NumCount,cc.SpaceCount,cc.OtherCount)
}
6.命令行参数(需要再次实践)
6.1看一个需求
我们希望能够获取到命令行输入的各种参数,该如何处理?
6.2基本介绍
os.Args是一个string 的切片,用来存储所有的命令行参数
https://golang.org/pkg/os/#Args
var (
// ErrInvalid indicates an invalid argument.
// Methods on File will return this error when the receiver is nil.
ErrInvalid = errInvalid() // "invalid argument"
ErrPermission = errPermission() // "permission denied"
ErrExist = errExist() // "file already exists"
ErrNotExist = errNotExist() // "file does not exist"
ErrClosed = errClosed() // "file already closed"
ErrNoDeadline = errNoDeadline() // "file type does not support deadline"
)
6.3举例说明
请编写一段代码,可以获取命令行各个参数
package main
import (
"fmt"
"os"
)
func main() {
//
fmt.Println("命令行参数的个数为:",len(os.Args))
//遍历 os.Args 切片,可以得到所有的命令行参数值
for k,v:=range os.Args{
fmt.Printf("args[%v]=%v\n",k,v)
}
}
6.4flag 包用来解析命令行参数
说明:前面的方式是比较原生的方式,对解析参数不是特别方便。特别是带有指定参数的命令行
比如:cmd>main.exe -f c:/111.txt -p200 -u root 这样的命令行
go设计者给我们提供了 flag包,可以方便解析命令行的参数,而且参数顺序可以随意
请编写一段代码,可以获取命令行各个参数
代码实现:
package main
import (
"flag"
"fmt"
)
func main() {
//定义几个变量,用于接收命令行参数值
var user string
var pwd string
var host string
var port int
//&user 就是接收用户命令行中输入的 -u 后面的参数值
//u 就是 -u 指定的参数
//" ",默认值
//用户名 ,默认为空 说明
flag.StringVar(&user,"u","","用户名,默认为空")
flag.StringVar(&pwd,"pwd","","密码,默认为空")
flag.StringVar(&host,"h","localhost","主机名,默认为本地")
flag.IntVar(&port,"port",8080,"端口默认 8080")
//这里必须转换
flag.Parse()
//输出
fmt.Printf("uer=%v pwd=%v host=%v port=%v",user,pwd,host,port)
}
7.json 基本介绍
JSON(JavaScript Object Notation )是一种轻量级的数据交换格式,易于阅读和编写,易于机器解析和生成k-v
JSON 是在 2001年开始推广使用的数据格式,目前已经成为主流的数据格式
JSON 是在 2001 年开始推广使用的数据格式,目前已经成为主流的数据格式
JSON易于机器解析和生成,并有效地提升网络传输效率,通常程序在网络传输时会先将数据(结构体,Map等)序列化成 JSON 字符串,到接收方得到json字符串时,在反序列化恢复原来的数据类型(结构体,map)这种方式已然成为各个语言的标准
Go-----序列化—JSON字符串—网络传输—程序----反序列化----其他语言
》应用场景(示意图)略
8.JSON数据格式说明
在js语言中,一切都是对象,因此,任何的数据类型都可以通过json来表示,例如;字符串,数字,对象,数组map,结构体
任何数据类型都可以转换成json格式
JSON 键值对是保存数据的一种方式
键值对组合中的键名写在前面并用双引号“”包裹,使用冒号:分隔,然后
紧接着值:
{“name":“zhangsan”,“pwd”,“123456”}\
9.Json数据在线解析
https://www.bejson.com/
网站可以转换json格式数据
10.JSON的序列化
介绍:
JSON序列化是指,将 key-value结构的数据类型(比如结构体,map,qi切片)序列化成 json字符串的操作
应用案例
结构体,map和切片的序列化,其他数据类型的序列化相似
代码:
package main
import (
"encoding/json"
"fmt"
)
//定义一个结构体
type Monster struct {
Name string
Age int
Birthday string
Sal float64
Skill string
}
func testStruct(){
monster:=Monster{
Name:"蛇精",
Age:2000,
Birthday:"20-01-02",
Sal:8000.0,
Skill:"缠人",
}
//将 monster序列化
data,err:=json.Marshal(&monster)
if err!=nil{
fmt.Printf("序列化错误 err=%v\n",err)
}
//输出序列化后的结果
fmt.Printf("monster序列化后=%v\n",string(data))
}
//将 map进行序列化
func testMap(){
//定义一个 map
var a map[string]interface{}
//使用map,需要make
a=make(map[string]interface{})
a["name"]="蜈蚣精"
a["age"]=88
a["address"]="吴老庄"
//将 a这个 map序列化
//将 monster序列化
data,err:=json.Marshal(a)
if err!=nil{
fmt.Printf("序列化错误 err=%v\n",err)
}
//输出序列化后的结果
fmt.Printf("a map 序列化=%v\n",string(data))
}
//演示对切片进行序列化,我们这个切片 []map[string]interface{}
func testSlice(){
var slice []map[string]interface{}
var m1 map[string]interface{}
//使用map前,需要先 make
m1=make(map[string]interface{})
m1["name"]="zhangsan"
m1["age"]="88"
m1["home"]="beijing"
slice=append(slice,m1)
var m2 map[string]interface{}
//使用 map前 ,需要先 make
m2=make(map[string]interface{})
//
m2["name"]="lisi"
m2["age"]="889"
m2["home"]=[2]string{"huoxing","yueqiu"}
slice=append(slice,m2)
//将切片进行序列化操作
data,err:=json.Marshal(slice)
if err!=nil{
fmt.Printf("序列化操作 err=%v\n",err)
}
//输出序列化后的结果
fmt.Printf("slice 序列化后=%v\n",string(data))
}
//对基本数据类型序列化,对基本数据类型进行序列化意义不大
func testFloat64(){
var num1 float64=23456.78
//对 num1 进行序列化
data,err:=json.Marshal(num1)
if err!=nil{
fmt.Printf("num1 序列化后=%v\n",string(data))
}
//输出序列化后的结果
fmt.Printf("num1 序列化后=%v\n",string(data))
}
func main() {
//把结构体,map,切片的序列化演示
testStruct()
testMap()
testSlice()
testFloat64()
}
注意事项:
对于结构体的序列化,如果我们希望序列化后的key的名字,又我们自己重新制定那么可以给struct指定一个 tag标签
//定义一个结构体
type Monster struct {
Name string `json:"monster_name"`//这运用了反射机制
Age int `json:"monster_age"`
Birthday string
Sal float64
Skill string
}
序列化后:
{"monster_name":"蛇精","monster_age":2000,"Birthday":"20-01-02","Sal":8000,"Skill":"缠人"}
11.json的反序列化
json反序列化,是指:
将 json字符串反序列化成对应的数据类型(比如:结构体,map,切片)
的操作
应用案例:
这里我们介绍一下json字符串反序列化成对应的数据类型
结构体,map,切片等
代码:
package main
import (
"encoding/json"
"fmt"
)
//定义一个结构体
type Monster struct {
Name string
Age int
Birthday string
Sal float64
Skill string
}
func unmarshalStruct(){
//str在项目开发中,是通过网络传输获取到的。。
str:="{\"Name\":\"蛇精\",\"Age\":2000,\"Birthday\":\"20-01-02\",\"Sal\":8000,\"Skill\":\"缠人\"}"
//定义一个妖怪实例
var monster Monster
err:=json.Unmarshal([]byte(str),&monster)
if err!=nil{
fmt.Printf("unmarshal err=%v\n",err)
}
fmt.Printf("反序列化后 monster=%v monster.Name=%v \n",monster,monster.Name)
}
//演示将 json字符串,反序列化成map
func unmarshalMap(){
str:="{\"address\":\"吴老庄\",\"age\":88,\"name\":\"蜈蚣精\"}"
//定义一个map
var a map[string]interface{}
//反序列化,
//反序列化map,不需要make,因为 make操作被封装到Unmarshal函数中
err:=json.Unmarshal([]byte(str),&a)
if err!=nil{
fmt.Printf("unmarshal er=%v\n",err)
}
fmt.Printf("反序列化后 a=%v\n",a)
}
//演示将 json字符串,反序列化成切片
func unmarshalSlice(){
str:="[{\"age\":\"88\",\"home\":\"beijing\",\"name\":\"zhangsan\"},{\"age\":\"889\",\"home\":[\"huoxing\",\"yueqiu\"],\"name\":\"lisi\"}]"
//定义一个 slice
var slice []map[string]interface{}
//反序列化,不需要make,因为 make 操作被封装到 unmarshal函数中
err:=json.Unmarshal([]byte(str),&slice)
if err!=nil{
fmt.Printf("unmarshal err=%v\n",err)
}
fmt.Printf("反序列化后 slice=%v\n",slice)
}
func main() {
unmarshalStruct()
unmarshalSlice()
unmarshalMap()
}
》对上面代码小结:
1》在反序列化一个json字符串时,要确保反序列化后的数据类型和
原来序列化前的数据类型一致
2》如果json 字符串是通过程序获取到的,则不需要再对 “转义处理
func unmarshalMap(){
//str:="{\"address\":\"吴老庄\",\"age\":88,\"name\":\"蜈蚣精\"}"
str:=testMap()
//定义一个map
var a map[string]interface{}
//反序列化,
//反序列化map,不需要make,因为 make操作被封装到Unmarshal函数中
err:=json.Unmarshal([]byte(str),&a)
if err!=nil{
fmt.Printf("unmarshal er=%v\n",err)
}
fmt.Printf("反序列化后 a=%v\n",a)
}
来源:CSDN
作者:大鸟蜀黍
链接:https://blog.csdn.net/tlkj6868xds/article/details/103808981