GO语言学习之文件操作

↘锁芯ラ 提交于 2020-01-11 23:40:44

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)

}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!