Go 文件操作
文件操作
创建文件
Go 语言中提供了一个 Create() 函数用于创建文件。
该函数在创建文件时,首先会判断要创建的文件是否存在,如果不存在,则创建,如果存在,会先将文件中已有的数据清空。
同时,当文件创建成功后,该文件会默认的打开,所以不用再执行打开操作,可以直接向该文件中写入数据。
创建文件的步骤:
- 导入 os 包,创建文件(读写文件的函数都在该包)
- 指定创建的文件存放路径以及文件名
- 执行 Create() 函数,进行文件创建
- 关闭文件
import (
"fmt"
"os"
)
func CreateFile(path string) {
//创建文件,返回两个值,一是创建的文件,二是错误信息
file, err := os.Create(path)
if err != nil {
fmt.Println("err=", err) // 有错误时则打印
return
}
defer file.Close() // 在退出函数时,关闭文件
}
func main() {
var filePath = "a.txt"
CreateFile(filePath)
}
写入数据
WriteString()
import (
"fmt"
"os"
)
func CreateFile(path string) {
//创建文件,返回两个值,一是创建的文件,二是错误信息
file, err := os.Create(path)
if err != nil {
fmt.Println("err=", err) // 有错误时则打印
return
}
for i := 0; i < 10; i++ {
// n表示写入的字符长度,err表示错误信息
n, err := file.WriteString("hello go\n") // 该方法默认不换行
if err != nil {
fmt.Println("err=", err) // 有错误时则打印
return
}
fmt.Println("write len=", n) // 9
}
defer file.Close() // 在退出函数时,关闭文件
}
func main() {
var filePath = "a.txt"
CreateFile(filePath)
}
Write()
使用 Write() 函数写数据时,参数为字节切片,因此所以需要将字符串转换成字节切片。该方法返回的也是写入文件数据的长度。
import (
"fmt"
"os"
)
func CreateFile(path string) {
//创建文件,返回两个值,一是创建的文件,二是错误信息
file, err := os.Create(path)
if err != nil {
fmt.Println("err=", err) // 有错误时则打印
return
}
for i := 0; i < 10; i++ {
// n表示写入的字符长度,err表示错误信息
str := fmt.Sprintf("i=%d\n", i) // 格式化字符串
buf := []byte(str)
n, err := file.Write(buf) // 该方法默认不换行
if err != nil {
fmt.Println("err=", err) // 有错误时则打印
return
}
fmt.Println("write len=", n) // 4
}
defer file.Close() // 在退出函数时,关闭文件
}
WriteAt()
该函数作用是在指定位置写入数据。
import (
"fmt"
"os"
)
func CreateFile(path string) {
//创建文件,返回两个值,一是创建的文件,二是错误信息
file, err := os.Create(path)
if err != nil {
fmt.Println("err=", err) // 有错误时则打印
return
}
for i := 0; i < 10; i++ {
// n表示写入的字符长度,err表示错误信息
str := fmt.Sprintf("i=%d\n", i)
buf := []byte(str)
// 找到文件末尾(偏移量)
offset, _ := file.Seek(0, os.SEEK_END)
// 从文件末尾(偏移量)写入内容
n, err := file.WriteAt(buf, offset)
if err != nil {
fmt.Println("err=", err) // 有错误时则打印
return
}
fmt.Println("write len", n) // 4
}
defer file.Close() // 在退出函数时,关闭文件
}
func main() {
var filePath = "a.txt"
CreateFile(filePath)
}
以上程序中 Seek() 函数返回值存储到变量 n 中,值为文件末尾的位置,且 WriteAt() 返回的也是写入的数据长度。
追加数据
对已经存在的文件不能再执行 Create(),而是要执行 OpenFile() 。
import (
"fmt"
"os"
)
func WriteFile(path string) {
//打开文件(文件路径, 模式, 权限)
file, err := os.OpenFile(path, os.O_APPEND, 6)
if err != nil {
fmt.Println("err=", err) // 有错误时则打印
return
}
n, err := file.WriteString("append hello go\n")
if err != nil {
fmt.Println("err=", err)
return
}
fmt.Println("write len", n)
defer file.Close()
}
func main() {
var filePath = "a.txt"
WriteFile(filePath)
}
常见的模式(参数二)有:
- O_RDONLY:只读模式
- O_WRONLY:只写模式
- O_RDWR:可读可写模式
- O_APPEND:追加模式
权限(参数三)表示如下:
- 0:没有任何权限
- 1:执行权限(如果是可执行文件,是可以运行的)
- 2:写权限
- 3:写、执行权限
- 4:读权限
- 5:读、执行权限
- 6:读、写权限
- 7:读、写、执行权限
读取文件
如果文件已经存在,并且也已经有数据了,那么可以直接读取该文件中的内容。
读取文件的基本流程如下:
- 打开要读取的文件
- 对文件进行读取
- 关闭文件
Read()
关于 Read() 函数的使用如下:
import (
"fmt"
"io"
"os"
)
func ReadFile(path string) {
file, err := os.Open(path)
if err != nil {
fmt.Println("err=", err)
return
}
buf := make([]byte, 1024) // 1k的大小
// n 表示从文件中读取的字符长度
n, err := file.Read(buf)
if err != nil && err != io.EOF { // 文件出错,且同时没有到结尾
fmt.Println("err=", err)
return
}
fmt.Println("buf=", string(buf[:n]))
defer file.Close()
}
func main() {
var filePath = "a.txt"
ReadFile(filePath)
}
行读取
import (
"bufio"
"fmt"
"io"
"os"
)
func ReadFile(path string) {
file, err := os.Open(path)
if err != nil {
fmt.Println("err=", err)
return
}
// 把内容先放缓冲区
r := bufio.NewReader(file)
// 循环读取文件内容,直至末尾
for {
// 遇到\n时读取并结束
buf, err := r.ReadBytes('\n')
if err != nil {
if err == io.EOF {
break
}
fmt.Println("err=", err)
}
// ReadBytes() 返回的是字节切片,所以在打印时要转换成字符串
fmt.Printf("buf=%s", buf)
}
defer file.Close()
}
func main() {
var filePath = "a.txt"
ReadFile(filePath)
}
案例:复制文件内容
需求:文件拷贝,将已有的文件复制一份,同时重新命名
实现思路:
- 让用户输入要拷贝的文件的名称(源文件)以及目的文件名称
- 创建目的文件
- 打开源文件,并且读取该文件中的内容
- 将从源文件中读取的内容写到目的文件中
import (
"fmt"
"io"
"os"
)
func CopyFile() {
var srcFile string
var dstFile string
fmt.Println("请输入源文件:")
fmt.Scan(&srcFile)
fmt.Println("请输入目的文件:")
fmt.Scan(&dstFile)
if srcFile == dstFile {
fmt.Println("源文件和目的文件不能相同")
return
}
// 只读方式打开源文件
openFile, err1 := os.Open(srcFile)
if err1 != nil {
fmt.Println("open err: ", err1)
return
}
// 创建目的文件
writeFile, err2 := os.Create(dstFile)
if err2 != nil {
fmt.Println("write err: ", err2)
return
}
// 从源文件读取内容
// 临时缓冲区
buf := make([]byte, 1024)
for {
n, err3 := openFile.Read(buf) // 每次读取1k内容
if err3 != nil {
// 文件读取完毕
if err3 == io.EOF {
fmt.Println("copy done")
break
}
fmt.Println("copy err: ", err3)
}
// 往目的文件写,赌多少写多少
writeFile.Write(buf[:n])
}
defer openFile.Close()
defer writeFile.Close()
}
func main() {
CopyFile()
}
目录操作
我们读写的文件一般存放于目录中。因此,有时需要指定到某一个目录下,根据目录存储的状况再进行文件的特定操作。
其实,目录也可以看成“文件”。通常我们读写的文件内容是可见的 ASCII 码,而目录文件的内容就是文件名和目录名,称之为目录项。我们读写目录文件,实质上就是在读写目录项。
打开目录
打开目录和打开文件一样,也是使用 OpenFile() 函数,但要指定不同的参数来通知系统,表示要打开的是目录文件。
func OpenFile(name string, flag int, perm FileMode) (*File, error)
- name:表示要打开的目录名称。使用绝对路径较多
- flag:表示打开文件的读写模式。可选择:
- O_RDONLY:只读模式
- O_WRONLY:只写模式
- O_RDWR:读写模式
- perm:表示打开权限(对于目录来说略有不同,通常传 os.ModeDir)
- 返回值:由于是操作目录,所以 file 是指向目录的文件指针,error 中保存错误信息
读目录内容
目录中存放的是文件名和子目录名,使用 Readdir() 函数来完成。
func (f *File) Readdir(n int) ([]FileInfo, error)
- n:表示读取目录的成员个数。通常传 -1,表示读取目录所有文件对象
- 返回值:FileInfo 类型的切片(其内部保存了文件名);error 保存错误信息
type FileInfo interface {
Name() string // base name of the file
Size() int64 // length in bytes for regular files; system-dependent for others
Mode() FileMode // file mode bits
ModTime() time.Time // modification time
IsDir() bool // abbreviation for Mode().IsDir()
Sys() interface{} // underlying data source (can return nil)
}
得到 FileInfo 类型切片后,可以 range 遍历切片元素,使用 .Name() 获取文件名,使用 .Size() 获取文件大小,使用 .IsDir() 判断文件是目录还是非目录文件。
示例:提示用户提供一个目录位置后,打开该目录,查看目录下的所有成员,并判断是文件还是目录
func main() {
fmt.Println("请输入要找寻的目录:")
var path string
fmt.Scan(&path) // 获取用户指定的目录名
// 只读打开该目录
dir, _ := os.OpenFile(path, os.O_RDONLY, os.ModeDir)
// 读取当前目录下所有的文件名和目录名,存入names切片
names, _ := dir.Readdir(-1)
// 遍历切片,获取文件/目录名
for _, name := range names {
if !name.IsDir() {
fmt.Println(name.Name(), "是一个文件")
} else {
fmt.Println(name.Name(), "是一个目录")
}
}
}
其他 API
目录操作还有其他的一系列 API,这里简单罗列几个较为常用的:
// 将当前工作目录修改为 dir 指定的目录
func Chdir(dir string) error
// 返回当前工作目录的绝对路径
func Getwd() (dir string, err error)
// 使用指定的权限和名称创建一个目录
func Mkdir(name string, perm FileMode) error
获取更多文件、目录操作 API,可查看 Go 标库文档。