本文首发于公众号:Hunter后端

原文链接:Golang基础笔记十四之文件操作

这一篇笔记介绍 Golang 里文件的相关操作,主要用的库是 io

以下是本篇笔记目录:

  1. 文件读取
  2. 文件写入
  3. 文件追加
  4. os.OpenFile()
  5. 文件属性

1、文件读取

1. 文件的打开与关闭

1) os.Open()

打开一个文件可以使用 os.Open() 函数,其代码示例如下:

filePath := "a.txt"
file, err := os.Open(filePath)

我们可以通过判断 err 是否等于 nil 来决定是否可以接着读取文件,假设这里的 filePath 不存在,那么 err 则的信息则会是:

open file fail, err: open a.txt: The system cannot find the file specified.

file 的具体读取操作在后面再介绍。

2) file.Close()

在打开文件后,我们可以使用 defer file.Close() 操作来确保文件最后会被正常关闭。

2. 文件内容的读取

在文件打开以后,介绍几种对文件进行读取的方式,以下是打开文件的代码:

filePath := "a.txt"
file, err := os.Open(filePath)
if err != nil {
fmt.Println("open file fail, err:", err)
return
}
defer file.Close()

假设 a.txt 文件内容为:

第一行
second_line
end of line
1) 一次性读取

如果目标文件不大,希望一次性读取文件内容的话,可以使用 io.ReadAll() 函数:

data, err := io.ReadAll(file)
if err != nil {
fmt.Println("read file error: ", err)
return
}
fmt.Println("read file data: ", string(data))
return

返回的结果是一个 []byte 类型,可以使用 string() 将其转换为字符串。

2) 分块读取

如果文件过大,我们可以分块读取,每次读取指定字节数的数据,下面提供一个示例,用于分批次读取文件内容,直到读完整个文件:

data := make([]byte, 6)
for {
count, err := file.Read(data)
if err == io.EOF {
fmt.Println("end of file, exit")
break
}
if err != nil {
fmt.Println("Error: ", err)
break
}
if count > 0 {
fmt.Println("read count: ", count, ", data: ", string(data[:count]))
}
}

这里我们定义了一个长度为 6 的 byte 数组,然后在 for 循环里一直使用 file.Read() 读取,每次都往 data 中填充数据,直到读取到文件末尾,或者读取出现 error。

在这里需要注意,Go 里对读取文件到末尾的信息包装成了一个 error,我们需要进行判断下。

file.Seek()

在读取文件内容的时候,我们还可以指定指针读取的位置,比如重置读取的指针到开头,我们可以如下操作:

file.Seek(0, io.SeekStart)

file.Seek() 函数接收两个参数,一个是偏移量,一个是起始位置,上面这行代码的含义就是从文件开头的偏移量为 0 的位置开始读取文件内容。

如果我们想从文件开头往后三个字节长度的地方开始读取,可以如下操作:

file.Seek(3, io.SeekStart)

而指定读取有三个参数:

const (
SeekStart = 0 // seek relative to the origin of the file
SeekCurrent = 1 // seek relative to the current offset
SeekEnd = 2 // seek relative to the end
)

分别表示文件开头,指针当前位置和文件末尾。

当然,file.Seek() 的第一个参数也可以是负数,比如我们想读取文件最后六个字节的内容,可以如下操作:

file.Seek(-6, io.SeekEnd)
3) 按行读取

我们可以使用 bufio.NewScanner() 函数来按行读取文件内容:

file.Seek(0, io.SeekStart)
scanner := bufio.NewScanner(file) for scanner.Scan() {
fmt.Println(scanner.Bytes(), scanner.Text())
}

这里,我们使用了两个内容,一个是 .Bytes(),一个是 .Text(),分别打印的内容是该行的字节数组和字符串数据。

2、文件写入

1. 文件的打开与关闭

文件写入的操作中,打开与关闭一个文件的操作如下:

filePath := "a.txt"
file, err := os.Create(filePath)
if err != nil {
fmt.Println("create file error: ", err)
return
} defer file.Close()

使用到的函数是 os.Create(),在这里,如果目标文件 filePath 不存在则会自动创建一个文件,如果存在,则会清空原来的数据,重新写入。

2. 文件内容的写入

文件打开以后,下面介绍几种写入内容的方式

1) file.Write()

可以直接使用 file 以字节数组的形式往文件写入数据:

    n, err := file.Write([]byte("first line\n"))
if err != nil {
fmt.Println("write error: ", err)
return
}
fmt.Printf("write %d bytes", n)

这里需要注意,如果要换行需要在末尾手动加上 \n 字符。

2) io.WriteString()

我们也可以使用 io.WriteString() 函数往文件里写入数据:

    n, err := io.WriteString(file, "first line\n")
if err != nil {
fmt.Println("write error: ", err)
return
}
fmt.Printf("write %d ", n)
3) bufio.NewWriter()

我们还可以使用 bufio.NewWriter() 函数写入,这种操作是以缓冲的形式写入,操作示例如下:

    writer := bufio.NewWriter(file)
n, err := writer.WriteString("first line\n")
if err != nil {
fmt.Println("write error: ", err)
return
}
writer.Flush()
fmt.Printf("write %d bytes\n", n)

这种操作需要在最后使用 writer.Flush() 函数将数据从缓冲区写入文件。

3、文件追加

1. 文件的打开与关闭

如果要对文件内容进行追加,我们可以使用 os.OpenFile() 函数,以下是一个使用示例:

filePath := "a.txt"

file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
fmt.Println("append file error: ", err)
return
}
defer file.Close()

在这里,os.OpenFile() 函数接受三个参数,第一个是文件地址,第二个是标志位,第三个是文件的权限。

对于标志位,这里的 os.O_APPEND、os.O_WRONLY、os.O_CREATE 分别表示追加,只写和创建,这样即便是文件不存在也不会报错,而是会创建一个新文件。

2. 文件内容的追加

追加操作可以使用 file.Write() 来写入字节数组,或者 file.WriteString() 写入字符串:

file.Write([]byte("hello write byte\n"))
file.WriteString("hello write string\n")

4、os.OpenFile()

这里再单独介绍一下 os.OpenFile() 函数,这个函数在前面追问文件内容的时候已经使用过一次了,这里着重再讲一下。

os.OpenFile() 函数是上面介绍的这些操作的基础函数,也就是说读取文件使用的 os.Open(),写入文件使用的 os.Create(),在底层的逻辑里都是调用的 os.OpenFile(),不过是在具体实现的时候,根据不同的目标,比如读取或者写入来传入不同的参数以实现具体功能。

先来介绍 os.OpenFile() 函数的参数。

1. os.OpenFile() 参数

这个函数接收三个参数,name,flag 和 perm。

1) name

name 就是文件名称,string 类型,表示我们需要操作的目标文件。

2) flag

flag 表示的是操作的目的,比如前面介绍追加文件的时候用到的 os.O_APPEND|os.O_WRONLY|os.O_CREATE

参数类型是 int,在源码里定义了一系列关于文件的操作,如下:

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.
O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened.
)

比如这里有 O_RDONLY 表示只读,O_WRONLY 表示只写,O_RDWR 表示读和写,在操作文件的时候,这几个参数之一是必传的,用来表示文件操作的目的是读或者写。

下面几个参数则需要和其他上面的几个参数之一合并使用,比如 O_APPEND 追加,O_CREATE 创建等。

上面我们介绍追加功能的时候,就是一个示例,内容是 os.O_APPEND|os.O_WRONLY|os.O_CREATE,这个操作首先通过必传的 os.O_WRONLY 表示是一个写操作,然后通过 os.O_APPEND 表示是追加操作,会在文件的末尾接着写入,而 os.O_CREATE 则表示如果目标文件不存在则创建一个新文件。

通过这种操作叠加的方式使我们追加文件的程序变得更健壮,不会因为文件不存在而报错。

3) perm

perm 表示权限,指的是我们操作文件的时候,对文件赋予的权限,和 Linux 上文件操作的权限是一致的,比如 0644 代表的含义是当前用户拥有可读可写,同用户组和其他用户组只拥有可读权限。

2. os.Open() 和 os.Create()

前面介绍了 os.Open() 和 os.Create() 分别用来读取和写入文件的操作示例,这两个函数背后也是通过调用 os.OpenFile() 来实现的。

1) os.Open()

os.Open() 函数在源代码中的定义如下:

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

可以看到通过 OpenFile() 给了一个只读的权限实现了读取文件内容的操作。

2) os.Create()

os.Create() 函数的源码如下:

func Create(name string) (*File, error) {
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

os.Create() 函数则是通过读写操作,不存在就创建文件操作,存在就对原文件进行截断操作的方式来实现写入。

3. os.ReadFile() 和 os.WriteFile()

除了上面介绍的读写操作,这里还介绍两个以 os.OpenFile() 函数为基础实现的读写操作,不过这两个函数的读和写都是一次性的,也就是会一次性读取文件全部内容,或者一次性写入全部内容。

1) os.ReadFile()

os.ReadFile() 函数操作示例如下:

    filePath := "a.txt"
data, err := os.ReadFile(filePath)
fmt.Println("data: ", string(data), ", err: ", err)

返回的是一个字节数组,如果想要按行进行切割,可以使用 strings.Split(string(data), "\n") 操作。

2) os.WriteFile()

os.WriteFile() 操作示例如下:

    err = os.WriteFile(filePath, []byte("hello write byte\nok write done\nlast line write\n"), 0644)
fmt.Println("write error: ", err)

这里将多行数据使用 \n 进行分隔。

5、文件属性

打开一个文件后,我们可以获取这个文件的相关属性。

可以如下操作:

filePath := "a.txt"
file, _ := os.Open(filePath) info, err := file.Stat()
if err != nil {
fmt.Println("error: ", err)
}
defer file.Close()

我们通过 file.Stat() 获取 FileInfo,文件的信息就都在 info 里了:

fmt.Println("文件名称: ", info.Name())
fmt.Printf("文件大小为 %d bytes\n", info.Size())
fmt.Printf("文件权限:%s, %o \n", info.Mode(), info.Mode())
fmt.Println("文件上次修改时间为:", info.ModTime())

这里文件权限打印出的字符串是 -rw-rw-rw-,然后我们打印其八进制的内容就是常见的 666 形式了。

Golang基础笔记十四之文件操作的更多相关文章

  1. MATLAB学习笔记(四)——文件操作

    首先,声明,如果学过C的话就可以不用看了,因为是一样的,只要注意一些系统变量的名字稍微变动了而已.都是基于ANSI C语言的标准库函数写的. (一)文件的打开与关闭 一.文件的打开 1.语法 fid= ...

  2. python学习 (三十四) Python文件操作

    1 写文件 my_list = ["] my_file = open("myfile.txt", "w") for item in my_list: ...

  3. 孤荷凌寒自学python第四十四天Python操作 数据库之准备工作

     孤荷凌寒自学python第四十四天Python操作数据库之准备工作 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 今天非常激动地开始接触Python的数据库操作的学习了,数据库是系统化设计 ...

  4. 《C++游戏开发》笔记十四 平滑过渡的战争迷雾(二) 实现:真正的迷雾来了

    本系列文章由七十一雾央编写,转载请注明出处.  http://blog.csdn.net/u011371356/article/details/9712321 作者:七十一雾央 新浪微博:http:/ ...

  5. X-Cart 学习笔记(四)常见操作

    目录 X-Cart 学习笔记(一)了解和安装X-Cart X-Cart 学习笔记(二)X-Cart框架1 X-Cart 学习笔记(三)X-Cart框架2 X-Cart 学习笔记(四)常见操作 五.常见 ...

  6. Python 函数基础、有序集合、文件操作(三)

    一.set 特点: set是一个无序且不重复的元素集合访问速度快:天生解决元素重复问题 方法: 初始化 >>> s1 = set()>>> print(type(s ...

  7. C语言第十二讲,文件操作.

    C语言第十二讲,文件操作. 一丶文件操作概述 在操作系统中,我们的文档都称为文件.操作系统也为我们提供了接口进行操作.不同语言都是使用的相同的接口,只不过封装的上层接口不一样 操作文件的步骤 打开文件 ...

  8. Java NIO 学习笔记(四)----文件通道和网络通道

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  9. Django笔记十四之统计总数、最新纪录和空值判断等功能

    本篇笔记将介绍一些 Django 查询中统计总数.最新纪录和空值判断等功能. count in_bulk latest.earliest first.last exists contains.icon ...

  10. Bootstrap<基础二十四> 缩略图

    Bootstrap 缩略图.大多数站点都需要在网格中布局图像.视频.文本等.Bootstrap 通过缩略图为此提供了一种简便的方式.使用 Bootstrap 创建缩略图的步骤如下: 在图像周围添加带有 ...

随机推荐

  1. Excel百万数据高性能导出方案!

    前言 在我们的日常工作中,经常会有Excel数据导出的需求. 但可能会遇到性能和内存的问题. 今天这篇文章跟大家一起聊聊Excel高性能导出的方案,希望对你会有所帮助. 1 传统方案的问题 很多小伙伴 ...

  2. 106套Axure RP大数据可视化大屏模板及通用组件库

    106套Axure RP大数据可视化大屏模板包括了多种实用美观的可视化组件库及行业模板库,行业模板涵盖:金融.教育.医疗.政府.交通.制造等多个行业提供设计参考. 随着大数据的发展,可视化大屏在各行各 ...

  3. Google Cloud Next大会上的耀眼新星:探索最具潜力的AI初创公司

    在拉斯维加斯举办的Google Cloud Next大会上,不仅揭晓了如Ironwood处理器和Gemini 2.5 Flash等重磅新技术,还展示了一系列使用谷歌云计算服务的最有趣的初创公司.这些创 ...

  4. C#中无法将文件”obj\debug\XXX.dll复制到“bin\Debug\XXX.dll” 拒绝访问

    较为方便有效的方法就是,把项目属性中的"应用程序集"的"程序集名称"修改为另一个名称即可.

  5. Ubuntu 初始设置:启用 root 账户、启用密码登录、启用Key登录、ssh超时、修改主机名

    新购买的Ubuntu服务器,默认禁用了root账户,如果想启用root账户的公钥登录,请使用以下方法: vi /etc/ssh/sshd_config # 编辑ssh配置文件 PermitRootLo ...

  6. Qt 的一个大坑:visual studio中setStyleSheet不支持jpg

    在代码中设置QWidget的背景图,一般会使用setStyleSeeht函数去设置样式表: border-image:("c:x.png"); 这里有个大坑:不支持jpg图片!

  7. Fiddler抓包工具安装HTTPS证书

    安装好之后,打开Fiddler根目录,打开CMD 执行以下内容 makecert.exe -r -ss my -n "CN=DO_NOT_TRUST_FiddlerRoot, O=DO_NO ...

  8. C# 锁机制全景与高效实践:从 Monitor 到 .NET 9 全新 Lock

    引言:线程安全与锁的基本概念 线程安全 在多线程编程中,保障共享资源的安全访问依赖于有效的线程同步机制.理解并处理好以下两个核心概念至关重要: 线程安全:指某个类.方法或数据结构能够在被多个线程同时访 ...

  9. kubernetes之RBAC介绍

    一.RBAC简单说明 在kubernetes中,授权有6种模式: ABAC(基于属性的访问控制) RBAC(基于角色的访问控制) Webhook Node AlwaysDeny(一直拒绝) Alway ...

  10. Linux安装以及JDK,Tomcat,mysql环境的搭建

    Linux操作系统以及JDK,tomcat,mysql环境的安装 linux特点 linux是一种开源的免费的操作系统 linux比windows 注重安全性.权限管理.稳定性.高并发处理的能力 li ...