• GO文件相关操作使用



    ​ 文件操作在实际开发中非常常见,这一部分我们来学习一下GO语言的文件操作。

    1 打开和关闭文件

    ​ 使用os.Open函数可以打开一个文件,该函数有两个返回值*File(文件的指针), error(打开文件是否有错误产生),该方法对应的原函数代码如下:

    func Open(name string) (*File, error) {
    	return OpenFile(name, O_RDONLY, 0)
    }
    
    • 1
    • 2
    • 3

    ​ 介绍完上面的函数之后,我们实际来编写一个例子,例子的代码如下:

    package main
    
    import (
    	"fmt"
    	"os"
    )
    
    func main() {
    	//绝对路径
    	filename := "D:\\code\\other\\go-project\\src\\project-study\\file-study-01\\ch01\\20220808001.txt"
    	readFromPath(filename)
    
    	//读取相对路径(工作空间相对路径)
    	filename = "file-study-01/ch01/20220808001.txt"
    	readFromPath(filename)
    
    	//读取相对路径(打成exe包后相对路径)
    	filename = "20220808001.txt"
    	readFromPath(filename)
    
    }
    
    /**
    * 通过文件名打开文件
    **/
    func readFromPath(filename string) {
    	file, err := os.Open(filename)
    	if err != nil {
    		fmt.Println("读取文件错误:", err)
    		return
    	}
    	defer file.Close()
    	fmt.Println("读取文件成功文件名:", file.Name())
    }
    
    
    • 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

    ​ 例子说明,本案例一共测试三个例子,第一个例子读取的是绝对路径,后两个例子是相对路径。其中第二个路径是相对工作空间路径,在本地执行会找到相关目录路径,打成exe包就找不到了;第三个例子恰好相反,在本地IDE里面启动会找不到对应路径,而在打成exe包中启动是可以找到的。

    1.1 IDE中测试

    ​ 在IDE中运行上面代码,代码在控制台中的输出如下:

    读取文件成功文件名: D:\code\other\go-project\src\project-study\file-study-01\ch01\20220808001.txt
    读取文件成功文件名: file-study-01/ch01/20220808001.txt
    读取文件错误: open 20220808001.txt: The system cannot find the file specified.
    
    • 1
    • 2
    • 3

    ​ 从运行结果上来看,前两个案例是找到了文件,后面一个案例没有找到文件。绝对路径能找到文件肯定是可以的,那么为什么第二个相对路径可以找到而第三个相对路径确找不到了?这其实和我们的开发的工作空间有关。

    ​ 打开配置信息,可以看到这里的工作目录是D:\code\other\go-project\src\project-study,所以就很容易理解了,为什么第二个例子的文件是可以找到的了。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XTFbSGQA-1659953606038)(D:\developsoftware\mayun\note\study-note\go\images\image-20220808151309604.png)]

    1.2 打成exe文件测试

    ​ 在IDE的命令行终端,进入到文件的开发目录,执行下面的打包指令go build main.go

    D:\code\other\go-project\src\project-study\file-study-01\ch01>go build main.go
    
    • 1

    ​ 然后再命令行终端,执行命令main.exe

    D:\code\other\go-project\src\project-study\file-study-01\ch01>main.exe
    读取文件成功文件名:%s D:\code\other\go-project\src\project-study\file-study-01\ch01\20220808001.txt
    读取文件错误:%v open file-study-01/ch01/20220808001.txt: The system cannot find the path specified.
    读取文件成功文件名:%s 20220808001.txt
    
    • 1
    • 2
    • 3
    • 4

    ​ 可以发现第三个例子可以找到文件,第二例子找不到了,这是因为打包后,文件相关目录和代码在一层的缘故。

    2 读取文件

    ​ GO语言在读取文件有三种常用的方式:通过file.Read读取一定长度的字节数组;通过reader.ReadString指定换行符读取;ioutil.ReadAll一次性读取文件中的所有内容。

    func (f *File) Read(b []byte) (n int, err error)
    
    func (b *Reader) ReadString(delim byte) (string, error)
    
    func ReadAll(r io.Reader) ([]byte, error)
    func ReadFile(filename string) ([]byte, error)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.1 读取固定长度的数据

    ​ 采用file.Read可以一次读取指定长度的字节数据。

    package main
    
    import (
    	"bufio"
    	"fmt"
    	"io"
    	"io/ioutil"
    	"os"
    )
    
    func main() {
        
    	filename := "D:\\code\\other\\go-project\\src\\project-study\\file-study-01\\ch02\\20220808002.txt"
        
    	file, err := os.Open(filename)
    	if err != nil {
    		fmt.Println("读取文件错误:", err)
    		return
    	}
    	defer file.Close()
    	fmt.Println("读取文件成功文件名:", file.Name())
    
    	fileReaderOnce(file)
    
    	defer file.Close()
    }
    
    /**
    * 从文件中读取 5个字节的数据
     */
    func fileReaderOnce(file *os.File) {
    	bytes := make([]byte, 5)
    	if n, err := file.Read(bytes); err != nil && err != io.EOF {
    		fmt.Println("读取文件内容异常:", err)
    	} else if err == io.EOF {
    		fmt.Println("文件读取完成")
    	} else {
    		fmt.Printf("读取文件内容数量%d,文件内容%s", n, string(bytes))
    	}
    }
    
    • 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

    ​ 上面代码事例指一次从文件中读取5个字节的数据。运行代码看控制台输出如下:

    读取文件成功文件名: D:\code\other\go-project\src\project-study\file-study-01\ch02\20220808002.txt
    读取文件内容数量5,文件内容12345
    
    • 1
    • 2

    2.2 循环读取数据

    ​ 可以采用循环的方式读取完文件中的内容,当返回的参读取数量n为0,错误err为io.EOF代表文件已经读取完毕。

    /**
    * 循环从文件中读取数据 每次读取5个字节的数据,只到读取完
     */
    func fileReaderLoop(file *os.File) {
    	bytes := make([]byte, 5)
    	for {
    		if n, err := file.Read(bytes); err != nil && err != io.EOF {
    			fmt.Println("读取文件内容异常:", err)
    			return
    		} else if err == io.EOF {
    			fmt.Println("文件读取完成")
    			break
    		} else {
    			fmt.Printf("读取文件内容数量%d,文件内容%s", n, string(bytes))
    			fmt.Println()
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    ​ 运行上面代码,看控制台输出内容如下(注意每行结尾的换行符(\n)算做两个字节)

    读取文件成功文件名: D:\code\other\go-project\src\project-study\file-study-01\ch02\20220808002.txt
    读取文件内容数量5,文件内容12345
    读取文件内容数量5,文件内容
    456
    读取文件内容数量5,文件内容78
    a
    读取文件内容数量5,文件内容bcdef
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.3 按行读取

    ​ 除了上面按照指定字节数读取文件中的内容,在实际的使用过程中,采用按行读取的场景会更多,可以采用reader.ReadString按行读取文件。

    /**
    * 按行读取文件中的数据
     */
    func bufioRead(file *os.File) {
    	reader := bufio.NewReader(file)
    	for {
    		var line string
    		var err error
    		//这里指定换行符为\n
    		if line, err = reader.ReadString('\n'); err != nil && err != io.EOF {
    			fmt.Println("读取文件内容异常:", err)
    			return
    		}
    		fmt.Print("当前行数据:", line)
    		if err == io.EOF {
    			fmt.Println("文件读取完成")
    			break
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    ​ 运行上面代码,控制台输出内容如下:

    读取文件成功文件名: D:\code\other\go-project\src\project-study\file-study-01\ch02\20220808002.txt
    当前行数据:12345
    当前行数据:45678
    当前行数据:abcdef文件读取完成
    
    • 1
    • 2
    • 3
    • 4

    2.4 一次性读取所有内容

    ​ 在文件本身不是很大的情况下,我们也可以采用一次性将文件中的内容全部读取出来方式处理文件。在这里就可以使用到ioutil.ReadAllioutil.ReadFile

    /**
    * 使用ioutil一次性读取文件中的所有内容
     */
    func ioutilRead(file *os.File) {
    	if bytes, err := ioutil.ReadAll(file); err != nil {
    		fmt.Println("读取文件内容异常:", err)
    	} else {
    		fmt.Println("读取到文件内容:", string(bytes))
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    ​ 运行上面代码,看控制到输出:

    读取文件成功文件名: D:\code\other\go-project\src\project-study\file-study-01\ch02\20220808002.txt
    读取到文件内容: 12345
    45678
    abcdef
    
    • 1
    • 2
    • 3
    • 4

    3 写文件

    ​ 可以采用os.OpenFile用来写文件,对应的函数结构如下:

    func OpenFile(name string, flag int, perm FileMode) (*File, error)
    
    • 1

    ​ 其中name:代表文件名;flag代表操作标识;perm代表文件权限,它是一个八进制数,r(读)04,w(写)02,x(执行)01。

    ​ flag的选项有下面这些,可以组合起来使用:

    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_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.   同步IO
    	O_TRUNC  int = syscall.O_TRUNC  // truncate regular writable file when opened. 清空
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.1 直接写

    ​ 可以通过file.Writefile.WriteString直接向文件中写入内容:

    package main
    
    import (
    	"bufio"
    	"fmt"
    	"io/ioutil"
    	"os"
    )
    
    func main() {
    	filename := "D:\\code\\other\\go-project\\src\\project-study\\file-study-01\\ch03\\20220808003.txt"
    	// 操作标识:没有文件就创建文件,有的话清空文件内容,只能写;文件权限: 可读可写
    	file, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
    	if err != nil {
    		fmt.Println("处理文件出错:", err)
    	}
    
    	write(file)
    
    	defer file.Close()
    }
    
    /**
    * 直接向文件中写入数据
     */
    func write(file *os.File) {
    	file.Write([]byte("我是祖国的花朵")) //写入字节切片数据
    	file.WriteString("太难了")       //直接写入字符串数据
    }
    
    • 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

    3.2 写缓冲区

    ​ 有的时候写的数据量比较大,可以采用先写到缓冲区,到一定数量后在一起刷到磁盘,可以减少IO的次数,提高性能。

    package main
    
    import (
    	"bufio"
    	"fmt"
    	"io/ioutil"
    	"os"
    )
    
    func main() {
    	filename := "D:\\code\\other\\go-project\\src\\project-study\\file-study-01\\ch03\\20220808003.txt"
    	// 操作标识:没有文件就创建文件,有的话清空文件内容,只能写;文件权限: 可读可写
    	file, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
    	if err != nil {
    		fmt.Println("处理文件出错:", err)
    	}
    
    
    	newWrite(file)
    
    
    	defer file.Close()
    }
    
    /**
    * 使用缓冲区,写的内容先写到缓冲区,然后通过Flush刷回磁盘
    * 本例子是一次性写满10条内容刷到磁盘
     */
    func newWrite(file *os.File) {
    	writer := bufio.NewWriter(file)
    	count := 0
    	for i := 0; i < 100; i++ {
    		writer.WriteString(fmt.Sprintf("我是dream21th %d", i) + "\n") //将数据先写入缓存
    		count++
    		if count == 10 {
    			writer.Flush() //将缓存中的内容写入文件
    			count = 0
    		}
    	}
    	writer.Flush() //将缓存中的内容写入文件
    }
    
    
    
    • 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

    3.3 ioutil写

    ​ 除了上面两张写方式外,还可以采用ioutil中的写文件方法:

    package main
    
    import (
    	"bufio"
    	"fmt"
    	"io/ioutil"
    	"os"
    )
    
    func main() {
    	filename := "D:\\code\\other\\go-project\\src\\project-study\\file-study-01\\ch03\\20220808003.txt"
    	// 操作标识:没有文件就创建文件,有的话清空文件内容,只能写;文件权限: 可读可写
    	file, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
    	if err != nil {
    		fmt.Println("处理文件出错:", err)
    	}
    
    	ioutilWriteFile()
    	defer file.Close()
    }
    
    
    /**
    *  向文件中写内容,每次写之前先清空文件内容
     */
    func ioutilWriteFile() {
    	filename := "D:\\code\\other\\go-project\\src\\project-study\\file-study-01\\ch03\\202208080031.txt"
    	str := "也许人生就是很累但是还必须坚持"
    	err := ioutil.WriteFile(filename, []byte(str), 0666)
    	if err != nil {
    		fmt.Println("写入文件出错:", err)
    		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
    • 32
    • 33
    • 34
    • 35

    4 其他操作

    4.1 判断文件是否存在

    ​ 可以通过下面代码判断文件是否存在:

    package main
    
    import (
    	"fmt"
    	"os"
    )
    
    func main() {
    	filename := "D:\\code\\other\\go-project\\src\\project-study\\file-study-01\\ch04\\20220808004.txt"
    
    	fmt.Println(fileIsExist(filename))
    }
    
    /**
    * 判断文件是否存在
     */
    func fileIsExist(filename string) bool {
    	if _, err := os.Stat(filename); err != nil {
    		return false
    	}
    	return true
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    4.2 删除文件

    ​ 可以通过下面的方式删除文件:

    /**
    * 删除指定文件
     */
    func fileDelete(filename string) bool {
    	if remove := os.Remove(filename); remove != nil {
    		return false
    	}
    	return true
    }
    
    /**
    * 删除指定文件夹及下面文件
     */
    func fileDeletes(filename string) bool {
    	if remove := os.RemoveAll(filename); remove != nil {
    		return false
    	}
    	return true
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    4.3 拷贝文件

    ​ 可以通过下面的方式拷贝文件:

    func copyFile() {
    	source := "D:\\code\\other\\go-project\\src\\project-study\\file-study-01\\ch04\\20220808004.txt"
    	target := "D:\\code\\other\\go-project\\src\\project-study\\file-study-01\\ch04\\202208080041.txt"
    
    	sourceFile, err := os.Open(source)
    	if err != nil {
    		fmt.Println("处理文件出错:", err)
    		return
    	}
    
    	targetFile, err := os.OpenFile(target, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
    
    	if err != nil {
    		fmt.Println("处理文件出错:", err)
    		return
    	}
    
    	io.Copy(targetFile, sourceFile)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    centos7安装keepalived 保证Nginx的高可用
    Hive面试题系列第一题-连续登录问题
    Java实现常见排序算法
    ArtCoder——通过风格转换生成多元化艺术风格二维码
    Java变量---尚硅谷Java入门视频学习
    pinia中使用@vueuse/core库的useStorage做数据的持久化存储
    MongoTemplate | 多条件查询
    【九度OJ】题目1434贪心算法
    【Kafka专题】Kafka快速实战以及基本原理详解
    MySQL 8.0 新特性之不可见主键
  • 原文地址:https://blog.csdn.net/qq_36305027/article/details/126233274