golang文件操作

设备文件有屏幕键盘等,标准输出就是屏幕,可以通过os.Stdout.Close()来阻止后面的程序输出内容,可以通过os.Stdint.Close()来阻止后面的程序通过键盘获取输入内容

文件的创建和写入

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
	path := "./test.txt"
	//创建文件,如果文件存在则打开文件且清空文件内容,它返回一个file的指针和一个error
	f, err := os.Create(path)
	if err != nil {
		//如果有错误就输出错误信息并且中止函数
		fmt.Println(err)
		return
	}
	//使用完毕记得要关闭文件,使用defer就可以在函数结束的前一刹那进行关闭
	defer f.Close()

	//Sprintln()可以将一行信息赋值给一个变量
	buf := fmt.Sprintln("我往文件内写入东西")
	//往文件内写入字符串,f代表文件的指针,WriteString就可以将内容追加到文件,注意是追加
	//它返回两个值,一个是写入的字节数,另一个是error
	n, err := f.WriteString(buf)
	if err != nil {
		fmt.Println(err)
		return
	}

image.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
	 fileObj,err:= os.OpenFile("./xxx.txt",os.O_CREATE|os.O_APPEND,0644)
	 if err!=nil{
		fmt.Println(err)
		return
	 }
	 fileObj.Write([]byte("强转string为字节切片"))

	//也可以用bufio进行写操作
	wr := bufio.NewWriter(fileObj)

	//写到缓存中
	wr.WriteString("hello\n")

	//这一步是将缓存中的内容写入文件,如果没有这一步,内容是在缓存中的,等程序执行完毕就会消失
	writer.Flush()

	//也可以使用ioutil.WriteFile将内容写入文件
	ioutil.WriteFile("./xx.txt",[]byte("写入的内容"),0666)

注意:在windows中,第三个参数文件权限可以随便写,因为这只针对于linux用户,os.O_CREATE|os.O_APPEND表示没有文件就创建,有文件就打开,内容是追加写入(它的实现方式是这两个参数分别对应一个十六进制的数,转成2进制后根据数的值进行操作),如CREATE是0x00040,二进制是0100 0000,APPEND是0x00400,二进制是0100 0000 0000,使用| 位运算符表示两位有一个1就为1,即最后的值是0100 0100 0000,这样就实现了传一个int参数有多个功能,可以进行多个位运算符

像只读和只写就是在同一位上一个为0一个为1,这样最后这一位就为1,只执行值为1的那个操作,就像linux的权限一样,每个位置的1代表不同的意义

os.O_CREATE|os.O_APPEND os.O_CREATE|os.O_TRUNC APPEND表示追加写入,TRUNC表示清空后写入

文件的读取

下面的代码可以实现指定读取多少内容,如果想直接读完整个文件,只需要在读取那里加一个for循环,它就会一直往下读取直到结尾出现error抛出EOF错误(EOF表示结尾),注意:抛出EOF的if语句要写到err !=nil中,因为EOF错误是它的子集,在err不为空中加return,而EOF中只需要加break,因为break触发后直接跳出循环了,就不会触发return了

尽量将关闭文件的defer写到err判定后面,因为如果先写defer,如果出现了err错误程序返回的*File是对应的零值nil,当程序结束时,defer中对于nil进行Close()函数就会造成panic错误(使用文件操作这里不会报错,因为close会返回一个error,但是如果调用的是第三方函数库,可能就会因此产生panic),而在err中写入return语句后,如果出现了err错误的话,直接返回,并不会执行到下面defer中的Close()函数

1
2
3
4
5
6
7
	if err1 != nil {
		if err1 == io.EOF{
			break
		}
		fmt.Println(err1)
		return
	}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	path := "./test.txt"
	//打开文件,返回一个文件指针和一个error
	f, err := os.Open(path)
	if err != nil {
		fmt.Println(err)
	}

	//函数运行结束时关闭文件
	defer f.Close()

	//创建一个2k长度的字节切片,表示读取的总量
	buf := make([]byte, 1024*10)

	//Read返回两个值,一个是字节数,一个是error
	//n是读取了多少文件字节数,如果buf大于文件总字节数,n返回总字节数。
	//如果总字节数大于buf,则n等于buf,即最多只能读取buf字节的内容,如果想多读取点就只能增大buf的长度值
	//Read()方法会将读取的内容放入buf中,后面查看buf内容即是查看文件内容
	n, err1 := f.Read(buf)
	if err1 != nil && err1 != io.EOF {	//文件出错并且没有读到结尾
		fmt.Println(err1)
		return
	}

	//可以用m:n来读取buf中间内容,n不能大于buf前面设定的值(即不能大于总长度),否则会报错
	//n可以设置常量,即表示查看buf容量中从m到n的内容
	//设置n则可以实现当文件总字节数小于buf量全部读取,大于则读取设置的最大值
	fmt.Print("buf:", string(buf[:n]))
	//buf中的值都是字节型的,可以通过强转变成string

	//不用println是原因是ln会进行换行,如果第一次读取并没有读取到行末尾,
	//ln还是会让它换行,这会导致读取内容有问题

如何解决fmt.Scanln()无法读取输入空格后的内容

Scanln()是以空白符分隔的,空白符就包括空格 回车等,如果输入的内容中含有空格,就只能读取到空格以前的内容,因此可以使用bufio来获取标准输入

1
2
3
4
5
	fmt.Println("请输入内容:")
	reader := bufio.NewReader(os.Stdin)
	//一直读,读到\n结束,返回读取到的内容
	str, _ := reader.ReadString('\n')
	fmt.Println("你输入的内容是:", str)

如何一行一行的进行读取且务必读完文件的全部内容(不论大小)

bufio包有一个NewReader函数可以为文件的读取创建一个缓冲区并返回一个缓冲区的指针

ReadBytes是缓冲区 *Reader的方法,读取缓冲区直到第一次遇到delim字节(即指定的关键值’\n’等),读取出一次后缓冲区中对应的内容就会消失,因此可以用for死循环来读取多行缓冲区内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
	path := "./test.txt"
	//打开文件,返回一个文件指针和一个error
	f, err := os.Open(path)
	if err != nil {
		fmt.Println(err)
	}
	//函数运行结束时关闭文件
	defer f.Close()

	//新建一个缓冲区,先把内容放进缓冲区里
	r := bufio.NewReader(f)

	for {
		//遇到\n就结束读取,buf为读取到的数据,这种会将\n也读取进去,所以使用Printf()
		buf, err1 := r.ReadBytes('\n')
		//当读到结尾即退出循环
		fmt.Printf("%v", string(buf))

		/*把err判断放到打印后面的原因是如果将它放在前面,它会先判断是否是最后一行,当处于最后
		一行时它直接跳出循环,最后一行的输出语句就不会进行,那么就会少输出一行
		*/
		if err1 != nil {
			if err1 == io.EOF {
				break
			}
			fmt.Println(err1)
		}
	}

使用ioutil直接读取整个文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
//它的底层用的依然是os.Open()的原理,文件的打开和defer关闭在底层已经设置好了,直接使用就行
func fileByioutil(){
	b,err:=ioutil.ReadFile("C:\\Users\\admin\\Desktop\\新建文件夹\\gold_control_config.xml")
	if err ==io.EOF{
		fmt.Println("读取完了")
		return
	}
	if err!=nil{
		fmt.Println("file err",err)
		return
	}
	fmt.Println(string(b))
}

实例:拷贝文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
	//获取命令行参数,判断是否为3个
	list := os.Args
	if len(list) != 3 {
		fmt.Println("拷贝格式为xx.exe src dst")
	}

	//比较目标文件和源文件是否同名
	srcName := list[1]
	dstName := list[2]
	if srcName == dstName {
		fmt.Println("源文件和目标文件不能相等")
	}

	//只读方式打开源文件
	sf, err1 := os.Open(srcName)
	if err1 != nil {
		fmt.Println(err1)
		return
	}

	//新建目标文件
	df, err2 := os.Create(dstName)
	if err2 != nil {
		fmt.Println(err2)
		return
	}

	//操作完毕要关闭文件
	defer sf.Close()
	defer df.Close()

	for {
		//读源文件
		buf := make([]byte, 1024*4)
		n, err3 := sf.Read(buf) //从源文件读取内容
		if err3 != nil {
			if err3 == io.EOF { //源文件读取完毕
				break
			}
			fmt.Println(err3)
			return
		}

		//写入目标文件,读多少写多少
		_, err4 := df.Write(buf[:n])
		if err4 != nil {
			fmt.Println(err3)
			return
		}
	}

如何在一个文件中间部分插入内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
	//打开源文件,第一次读取到要插入的点
	//读取完后下次读取时光标会在此处
	f1,_:=os.Open("./a.txt")
	buf:=make([]byte,1)
	n,_:=f1.Read(buf)

	//创建临时文件,并将读取的内容写入进去
	f2,_:=os.Create("./b.txt")
	f2.Write(buf[:n])

	//写入自己需要插入的内容
	f2.Write([]byte("哈哈哈哈哈"))

	//读取源文件后面的部分,如果多就需要使用for循环
	buf1:=make([]byte,1024)
	n1,err:=f1.Read(buf1)

	//将源文件后半部分写入临时文件
	f2.Write(buf1[:n1])
	if err!=nil{
		return
	}
	f2.Close()
	f1.Close()

	//现在f2临时文件就有插入想要的内容,通过rename重命名成源文件实现覆盖
	os.Rename("./b.txt","./a.txt")
Licensed under CC BY-NC-SA 4.0