Golang: 常用的文件读写操作
Go 语言提供了很多文件操作的支持,在不同场景下,有对应的处理方式,今天就来系统地梳理一下,几种常用的文件读写的形式。
一、读取文件内容
1、按字节读取文件
这种方式是以字节为单位来读取,相对底层一些,代码量也较大,我们看下面代码:
// read-bytes.go
package main
import (
"fmt"
"io"
"os"
)
func main() {
file, _ := os.Open("test.txt")
defer file.Close()
// 字节切片缓存 存放每次读取的字节
buf := make([]byte, 1024)
// 该字节切片用于存放文件所有字节
var bytes []byte
for {
// 返回本次读取的字节数
count, err := file.Read(buf)
// 检测是否到了文件末尾
if err == io.EOF {
break;
}
// 取出本次读取的数据
currBytes := buf[:count]
// 将读取到的数据 追加到字节切片中
bytes = append(bytes, currBytes...)
}
// 将字节切片转为字符串 最后打印出来文件内容
fmt.Println(string(bytes))
}
2、结合 ioutil 来读取
如果我们不想那么麻烦,可以结合 ioutil
来精简上面的代码:
// read-with-util.go
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
file, _ := os.Open("test.txt")
defer file.Close()
// ReadAll接收一个io.Reader的参数 返回字节切片
bytes, _ := ioutil.ReadAll(file)
fmt.Println(string(bytes))
}
由于 os.File 也是 io.Reader 的实现,我们可以调用 ioutil.ReadAll(io.Reader)
方法,将文件所有字节读取出来,省去了使用字节缓存循环读取的过程。
3、仅使用 ioutil 包来完成读取操作:
为了进一步简化文件读取操作,ioutil 还提供了 ioutil.ReadFile(filename string)
方法,一行代码搞定读取任务:
// read-by-util.go
package main
import (
"fmt"
"io/ioutil"
)
func main() {
bytes, _ := ioutil.ReadFile("test.txt")
fmt.Println(string(bytes))
}
4、逐行读取:
有时候为了便于分析处理,我们希望能够逐行读取文件内容,这个时候可以 Scanner 来完成:
// read-line-by-line.go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, _ := os.Open("test.txt")
defer file.Close()
// 接受io.Reader类型参数 返回一个bufio.Scanner实例
scanner := bufio.NewScanner(file)
var count int
for scanner.Scan() {
count++
// 读取当前行内容
line := scanner.Text()
fmt.Printf("%d %s\n", count, line)
}
}
这简直就是 java.util.Scanner 翻版嘛,并且 Go 语言中的 Scanner 也可以接收不同的输入源,比如 os.Stdin 等。
上面代码直接打印出了每一行的数据,如果大家想得到最终文件的内容,可以创建一个字符串切片,每次逐行扫描时,将当前行内容追加到切片中即可。
以上就是几种常用的文件读取方式,当然还有其他更高级的方式,有机会再做总结。
二、写入文件操作
1、使用 ioutil 完成写入操作
上面我们介绍了 ioutil.ReadFile(filename string),与之对应地有 ioutil.WriteFile(filename string, ...)
方法,可以轻松完成写入操作:
// write-by-util.go
package main
import (
"io/ioutil"
)
func main() {
data := []byte("hello goo\n")
// 覆盖式写入
ioutil.WriteFile("test.txt", data, 0664)
}
我们看到,WriteFile() 方法需要传入三个参数,它的完整签名是:ioutil.WriteFile(filename string, data []byte, perm os.FileMode)。如果文件不存在,则会根据指定的权限创建文件,如果存在,则会先清空文件原有内容,然后再写入新数据。
需要注意最后一个参数,它是一个无符号 32 位整数,表示当前文件的权限,也是标准的 Unix 文件权限格式。
Unix 使用 -rwxrwxrwx
这样的形式来表示文件权限,其中:
- 第1位:文件属性,
-
表示是普通文件,d
表示是一个目录 - 第2-4位:文件所有者的权限
- 第5-7位:文件所属用户组的权限
- 第8-10位:其他人的权限
在权限设置中:
r
表示 read,值为4
w
表示 write,值为2
x
表示 exec,值为1
下面我们通过 os.FileMode
测试一下:
package main
import (
"fmt"
"os"
)
func showMode(code int) {
fmt.Println(os.FileMode(code).String())
}
func main() {
showMode(0777)
showMode(0766)
showMode(0764)
}
运行程序,控制台打印如下:
-rwxrwxrwx
-rwxrw-rw-
-rwxrw-r--
2、通过File句柄完成写入操作
上面我们曾使用过 os.Open(name string) 方法,这个方法是以只读方式打开文件的,os 包还提供了 os.OpenFile(name string, flag int, perm FileMode) 方法,通过指定额外的 读写方式
和 文件权限
参数,使文件操作变得更为灵活。
其中,flag 有以下几种常用的值:
- os.O_CREATE: create if none exists 不存在则创建
- os.O_RDONLY: read-only 只读
- os.O_WRONLY: write-only 只写
- os.O_RDWR: read-write 可读可写
- os.O_TRUNC: truncate when opened 文件长度截为0:即清空文件
- os.O_APPEND: append 追加新数据到文件
在打开文件之后,我们可以通过 Write() 和 WriteString() 方法写入数据,最后通过 Sync() 方法将数据持久化到磁盘:
// write-by-file-descriptor.go
package main
import (
"fmt"
"os"
)
// 打印写入的字节数
func printWroteBytes(count int) {
fmt.Printf("wrote %d bytes\n", count)
}
func main() {
// 以指定的权限打开文件
file, _ := os.OpenFile("test2.txt", os.O_RDWR | os.O_APPEND | os.O_CREATE, 0664)
defer file.Close()
data := []byte("hello go\n")
// 写入字节
count, _ := file.Write(data)
printWroteBytes(count)
// 写入字符串
count, _ = file.WriteString("hello world\n")
printWroteBytes(count)
// 确保写入到磁盘
file.Sync()
}
3、通过bufio包完成写入操作
这种方式其实是在 File 句柄上做了一层封装,调用方式和上面直接写入非常相似,大家仅做个参考:
// write-with-bufio.go
package main
import (
"bufio"
"fmt"
"os"
)
func printWroteBytes(count int) {
fmt.Printf("wrote %d bytes\n", count)
}
func main() {
file, _ := os.OpenFile("test.txt", os.O_RDWR | os.O_APPEND | os.O_CREATE, 0664)
defer file.Close()
// 获取bufio.Writer实例
writer := bufio.NewWriter(file)
// 写入字符串
count, _ := writer.Write([]byte("hello go\n"))
fmt.Printf("wrote %d bytes\n", count)
// 写入字符串
count, _ = writer.WriteString("hello world\n")
fmt.Printf("wrote %d bytes\n", count)
// 清空缓存 确保写入磁盘
writer.Flush()
}
以上就是常用的文件读写方式,今天就总结到这里吧,后续有机会再探讨更多内容。