Go文件操作
对于文件,我们并不陌生,文件是数据源(保存数据的地方)的一种,比如大家经常使用的word文档,txt文件,Excel文件...等等都是文件。文件最主要的作用就是保存数据,它既可以保存一张图片,也可以保存视频,声音......
文件在程序中是以流的形式来操作的。
流:数据在数据源(文件)和程序(内存)之间经历的路径
输出流:数据从程序(内存)到数据源(文件)的路径
输入流:数据从数据源(文件)到程序(内存)的路径
输入与输出都是相对于内存而言的,从内存向外流就是输出,从外部向内存流就是输入
在Go中,我们操作文件的方法在os包中,会经常使用到os.File结构体 Go语言标准库文档
示例1: 打开和关闭文件
package main
import (
"fmt"
"os"
)
func main() {
//打开文件(/Users/xxx/Go/src/file.txt)
//概念说明:file的叫法
//1.file 叫 file对象
//2.file 叫 file指针
//3.file 叫 file文件句柄
file, err := os.Open("/Users/itbsl/Go/src/file.txt")
if err != nil {
fmt.Println("文件打开失败,原因是:", err)
//os.Exit(0)
}
defer func() {
//文件及时关闭
err = file.Close()
if err != nil {
fmt.Println("文件关闭失败,原因是", err)
}
}()
}
示例2: 打开文件并读取内容
使用Read()函数按照字节读
package main
import (
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("./test.txt")
if err != nil {
fmt.Printf("open file failed, err:%v\n", err)
return
}
defer func() {
err = file.Close()
if err != nil {
fmt.Printf("close file failed, err:%v\n", err)
}
}()
var content []byte
var tmp = make([]byte, 128)
for {
n, err := file.Read(tmp)
//为什么是tmp[:n]而不是tmp[:]?
//因为当读取到最后一行的内容长度不足tmp的长度的时候
//新读取的内容只会覆盖前半部分上次读取到的tmp的内容,
//后半部分还是上一次读取的内容,如果用tmp[:]就会导致
//后半部分久内容又会被重新赋值一次,这其实是错误的
content = append(content, tmp[:n]...)
if err == io.EOF {//读到文件末尾
break
}
}
fmt.Printf("读取出来的内容为:\n")
fmt.Printf("%q\n", string(content))
}
示例3: 一次性读取文件
读取文件内容并显示在终端,将文件内容一次性读取到终端,适用于文件不大的情况。
package main
import (
"fmt"
"io/ioutil"
)
func main() {
//打开文件,文件路径相对于GOPATH开始,或者写全路径(/Users/xxx/Go/src/file.txt)
file, err := ioutil.ReadFile("src/file.txt")
if err != nil {
fmt.Println("文件打开失败,原因是:", err)
}
fmt.Printf("%s", string(file))
}
示例4: 带缓冲的Reader读文件
读取文件的内容并显示在终端(带缓冲区的方式),使用os.Open
, file.Close
,bufio.NewReader
,reader.ReadString
函数和方法。适合读取大文件
1.使用ReadBytes方法
代码1:
package main
import (
"bufio"
"fmt"
"io"
"log"
"os"
)
func main() {
file, err := os.Open("./test.txt")
if err != nil {
log.Fatalf("open file failed, err: %v\n", err)
}
defer func() {
err = file.Close()
if err != nil {
log.Fatalf("close file failed, err: %v\n", err)
}
}()
//定义变量result用来存储读取结果
var result string
//创建一个带有缓冲区的reader
reader := bufio.NewReader(file)
for {
buf, err := reader.ReadBytes('\n')
if err != nil && err == io.EOF { //EOF代表文件的末尾
//注意:为什么要判断err是否等于io.EOF?
//因为存在这种情况,文件有内容的最后那一行尾部没有换行
//当使用ReadBytes或者ReadString方法按照'\n'换行读取时,读到尾部没有换行的这种情况时就会报io.EOF错误
//此时buf是读取到了内容的,如果忽略掉了,那么最终的读取结果会少了最后一行的内容
result += string(buf)
break
}
result += string(buf)
}
fmt.Println(result)
}
代码2:
package main
import (
"bufio"
"fmt"
"io"
"log"
"os"
)
func main() {
file, err := os.Open("./test.txt")
if err != nil {
log.Fatalf("open file failed, err: %v\n", err)
}
defer func() {
err = file.Close()
if err != nil {
log.Fatalf("close file failed, err: %v\n", err)
}
}()
//定义变量result用来存储读取结果
var result string
//创建一个带有缓冲区的reader
reader := bufio.NewReader(file)
for {
buf, err := reader.ReadBytes('\n')
if err != nil {
if err == io.EOF { //EOF代表文件的末尾
//注意:为什么要判断err是否等于io.EOF?
//因为存在这种情况,文件有内容的最后那一行尾部没有换行
//当使用ReadBytes或者ReadString方法按照'\n'换行读取时,读到尾部没有换行的这种情况时就会报io.EOF错误
//此时buf是读取到了内容的,如果忽略掉了,那么最终的读取结果会少了最后一行的内容
result += string(buf)
break
} else {
log.Fatalf("ReadBytes failed, err: %v\n", err)
}
}
result += string(buf)
}
fmt.Println(result)
}
2.ReadString方法
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
//打开文件
file, err := os.Open("./files/test.txt")
if err != nil {
fmt.Println("文件打开失败,原因是:", err)
return
}
//当函数退出时,要及时的关闭file
defer func() {
//文件及时关闭
err = file.Close()
if err != nil {
fmt.Println("文件关闭失败,原因是", err)
}
}()
//创建一个 *Reader,是带缓冲的
reader := bufio.NewReader(file)
var result string
//循环读取文件内容
for {
str, err := reader.ReadString('\n') //读到一个换行就结束
result += str
if err == io.EOF {//io.EOF代表文件的末尾
//注意:如果文件最后一行文字没有换行,则会一直读取到文件末尾,
//所以即使是判断读到了文件末尾,也要把读取的内容输出一下
break
}
}
fmt.Println(result)
}
示例5: 创建文件并写入内容
第二个参数:文件代开模式(可以组合);第三个参数:权限控制(如0755)
package main
import (
"fmt"
"os"
)
func main() {
//1.创建文件file.txt
file, err := os.OpenFile("src/file.txt", os.O_WRONLY | os.O_CREATE, 0755)
if err != nil {
fmt.Println("文件打开/创建失败,原因是:", err)
return
}
defer func() {
err = file.Close()
if err != nil {
fmt.Println("文件关闭失败,原因是:", err)
}
}()
//写入数据
var str = "暗黑西游狮驼岭,斗战胜佛孙悟空。\n"
for i := 0; i < 5; i++ {
file.WriteString(str)
}
}
示例6: 写文件的四种方式
1.使用WriteAt()搭配Seek()方法实现写文件功能
package main
import (
"io"
"log"
"os"
)
func main() {
file, err := os.OpenFile("./test.txt", os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
log.Fatalf("open file failed, err: %v\n", err)
}
defer func() {
err = file.Close()
if err != nil {
log.Fatalf("close file failed, err: %v\n", err)
}
}()
//Seek(): 修改文件的读写指针位置.
//参数1: 偏移量. 正:向文件尾部偏移, 负:向文件头部偏移
//参数2: 偏移起始位置
// io.SeekStart: 文件起始位置
// io.SeekCurrent: 文件当前位置
// io.SeekEnd: 文件结尾位置
//返回值:表示从文件起始位置,到当前文件读写指针位置的偏移量。
//WriteAt(): 在文件指定偏移位置,写入[]byte,通常搭配Seek()
//参数1: 待写入的数据
//参数2: 偏移量
//返回: 实际写出的字节数
for i := 0; i < 5; i++ {
offset, _ := file.Seek(-3, io.SeekEnd)
_, _ = file.WriteAt([]byte("你好"), offset)
}
}
注意: 由于使用的OpenFile函数打开的文件,所以在选择打开模式的时候不能选择os.O_APPEND
模式,因为该模式表示的是在文件末尾追加,这与WriteAt在指定的位置写是想冲突的,虽然我在测试的时候加上os.O_APPEND
模式并没有报错,但是代码执行完之后发现,想要写入的内容并没有真正的写入到文件中。
写入前
写入后
2.一次性写文件
package main
import (
"io/ioutil"
"log"
)
func main() {
str := "hello树先生"
//如果文件已存在,则会清空原来的内容,写入新内容,如果文件不存在,则会创建文件并写入内容
err := ioutil.WriteFile("./test.txt", []byte(str), 0755)
if err != nil {
log.Fatalf("写入文件错误,错误为:%v\n", err)
}
}
3.使用带缓冲的方式写文件
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
//1.创建文件file.txt
file, err := os.OpenFile("src/file.txt", os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0755)
defer func() {
err = file.Close()
if err != nil {
fmt.Println("文件关闭失败,原因是:", err)
}
}()
if err != nil {
fmt.Println("文件创建失败,原因是:", err)
return
}
//写入数据
var str = "你好,世界\n"
//写入时,使用带缓存的*Writer
writer := bufio.NewWriter(file)
for i := 0; i < 5; i++ {
writer.WriteString(str)
}
//因为writer是带缓存的,因此在调用writeString方法时,其实内容是先写入到缓存
//因此需要调用Flush方法,将缓存数据写入到文件中,否则文件中会丢失数据
writer.Flush()
}
示例7: 把一个文件内容写入到另一个文件
package main
import (
"fmt"
"io/ioutil"
)
func main() {
//打开文件,文件路径相对于GOPATH开始,或者写全路径(/Users/xxx/Go/src/file.txt)
data, err := ioutil.ReadFile("src/1.txt")
if err != nil {
fmt.Println("文件打开失败,原因是:", err)
}
err = ioutil.WriteFile("src/2.txt", data, 0755)
if err != nil {
fmt.Println("文件写入失败,原因是:", err)
}
}
示例8:使用bufio获取用户输入
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
var s string
var reader = bufio.NewReader(os.Stdin)
s, _ = reader.ReadString('\n')
fmt.Printf("读取到的内容为:%s\n", s)
}
示例9: 判断文件或目录是否存在
Go判断文件或文件夹是否存在的方法为使用os.Stat()函数返回的错误值进行判断:
(1)如果返回的错误为nil,说明文件或文件夹存在
(2)如果返回的类型使用os.IsNotExist()判断为true,说明文件或文件夹不存在
(3)如果返回的错误为其它类型,则不确定是否存在
package main
import (
"fmt"
"os"
)
func main() {
isExist, err := isFileExists("src/sfile.txt")
if err != nil {
fmt.Println("发生错误:", err)
}
if isExist {
fmt.Println("存在")
} else {
fmt.Println("不存在")
}
}
//判断文件或者目录是否存在
func isFileExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
示例10: 拷贝文件、图片音视频
io.Copy方法
package main
import (
"fmt"
"io"
"os"
)
func CopyFile(srcFileName string, dstFileName string) (int64, error) {
//源文件处理
srcFile, err := os.Open(srcFileName)
defer func() {
err = srcFile.Close()
if err != nil {
fmt.Println("源文件关闭失败,原因是:", err)
}
}()
if err != nil {
fmt.Println("源文件打开失败,原因是:", err)
return 0, err
}
//目标文件处理
dstFile, err := os.OpenFile(dstFileName, os.O_CREATE | os.O_WRONLY, 0755)
defer func() {
err = dstFile.Close()
if err != nil {
fmt.Println("目标文件关闭失败,原因是:", err)
}
}()
if err != nil {
fmt.Println("目标文件打开失败,原因是:", err)
return 0, err
}
return io.Copy(dstFile, srcFile)
}
func main() {
result, err := CopyFile("src/dst.jpeg", "src/哈哈.jpeg")
if err == nil {
fmt.Println("拷贝成功!拷贝的字节数为: ", result)
}
}
对于大文件,我们还可以采用下面的方式
package main
import (
"io"
"log"
"os"
)
func CopyFile(srcFileName string, dstFileName string) {
//打开源文件
srcFile, err := os.Open(srcFileName)
if err != nil {
log.Fatalf("源文件读取失败,原因是:%v\n", err)
}
defer func() {
err = srcFile.Close()
if err != nil {
log.Fatalf("源文件关闭失败,原因是:%v\n", err)
}
}()
//创建目标文件,稍后会向这个目标文件写入拷贝内容
distFile, err := os.Create(dstFileName)
if err != nil {
log.Fatalf("目标文件创建失败,原因是:%v\n", err)
}
defer func() {
err = distFile.Close()
if err != nil {
log.Fatalf("目标文件关闭失败,原因是:%v\n", err)
}
}()
//定义指定长度的字节切片,每次最多读取指定长度
var tmp = make([]byte, 1024*4)
//循环读取并写入
for {
n, err := srcFile.Read(tmp)
n, _ = distFile.Write(tmp[:n])
if err != nil {
if err == io.EOF {//读到了文件末尾,并且写入完毕,任务完成返回(关闭文件的操作由defer来完成)
return
} else {
log.Fatalf("拷贝过程中发生错误,错误原因为:%v\n", err)
}
}
}
}
func main() {
CopyFile("./worm.mp4", "./dist.mp4")
}
示例11: 遍历目录
遍历目录
package main
//我们读写的文件一般存放于目录中.因此,有时需要指定到某一个目录下,根据目录存储的状况
//再进行文件的特定操作.接下来我们看看目录的基本操作方法.
import (
"fmt"
"log"
"os"
)
//打开目录
//打开目录我们也使用OpenFile函数,但要指定不同的参数来通知系统,要打开的是一个目录文件.
//func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
//参数1: name,表示要打开的目录名称.使用绝对路径较多
//参数2: flag,表示打开文件的读写模式
//参数3: perm,表示打开权限.但对于目录来说有所不同,通常传os.ModeDir.
//返回值:由于是操作目录,所以file是指向目录的文件指针.err中保存错误信息
//读目录内容
//这与读文件有所不同.目录中存放的是文件名和子目录名.所以使用Readdir函数
//func (f *File) Readdir(n int) (fi []FileInfo, err error)
//如果n>0,Readdir函数会返回一个最多n个成员的切片。这时,如果Readdir返回一个空切片,
//它会返回一个非nil的错误说明原因。如果到达了目录f的结尾,返回值err会是io.EOF。
//
//如果n<=0,Readdir函数返回目录中剩余所有文件对象的FileInfo构成的切片。
//此时,如果Readdir调用成功(读取所有内容直到结尾),它会返回该切片和nil的错误值。
//如果在到达结尾前遇到错误,会返回之前成功读取的FileInfo构成的切片和该错误。
func main() {
//不推荐,因为通过查看ioutil.ReadDir()函数可知,官方使用的是os.Open()函数打开的目录
//file, err := os.OpenFile("./dir", os.O_RDWR, os.ModeDir)
file, err := os.Open("./dir")
if err != nil {
log.Fatalf("文件打开失败,原因是:%v\n", err)
}
defer func() {
err = file.Close()
if err != nil {
log.Fatalf("文件关闭失败,原因是:%v\n", err)
}
}()
//Readdir方法返回一个FileInfo接口类型的切片和一个error类型的错误
infos, err := file.Readdir(-1)
for _, info := range infos {
fmt.Printf("%v, %v\n", info.Name(), info.IsDir())
}
}
仅遍历目录,忽略文件
方法1:使用os包
package main
import (
"fmt"
"os"
)
var dirNames = make([]string, 0, 50)
var pathSeparator = string(os.PathSeparator)
func traverseDir(filePath string) error {
file, err := os.Open(filePath)
if err != nil {
return err
}
fileInfo, err := file.Readdir(0)
if err != nil {
return err
}
for _, value := range fileInfo {
if value.IsDir() {
dirNames = append(dirNames, value.Name())
err = traverseDir(filePath+pathSeparator+value.Name())
if err != nil {
return err
}
}
}
return err
}
func main() {
var filePath = "./dir"
err := traverseDir(filePath)
if err != nil {
fmt.Println(err)
}
fmt.Println(dirNames)
}
方法2:使用ioutil包
package main
import (
"fmt"
"io/ioutil"
"os"
)
var dirNames = make([]string, 0, 50)
var pathSeparator = string(os.PathSeparator)
func traverseDir(filePath string) error {
fileInfos, err := ioutil.ReadDir(filePath)
if err != nil {
return err
}
for _, fileInfo :=range fileInfos {
if fileInfo.IsDir() {
dirNames = append(dirNames, fileInfo.Name())
err = traverseDir(filePath+pathSeparator+fileInfo.Name())
if err != nil {
return err
}
}
}
return err
}
func main() {
var filePath = "./dir"
err := traverseDir(filePath)
if err != nil {
fmt.Println(err)
}
fmt.Println(dirNames)
}
示例12: 修改文件名
package main
import (
"fmt"
"io/ioutil"
"os"
"strings"
)
var pathSeparator = string(os.PathSeparator)
//重命名文件
func renameFileName(filePath string, old string, new string) error {
files, err := ioutil.ReadDir(filePath)
if err != nil {
return err
}
for _, fileInfo := range files {
if !fileInfo.IsDir() {
err = os.Rename(filePath + pathSeparator + fileInfo.Name(),
filePath + pathSeparator + strings.Replace(fileInfo.Name(), old, new, -1),
)
if err != nil {
return err
}
}
}
return err
}
func main() {
var filePath = "./dir"
err := renameFileName(filePath, "f", "kkk")
if err != nil {
fmt.Printf("错误: %v\n", err)
}
}
示例13:创建目录
package main
import (
"fmt"
"os"
)
func main() {
//Mkdir使用指定的权限和名称创建一个目录。如果出错,会返回*PathError底层类型的错误。
err := os.Mkdir("./foo", 0755)
if os.IsExist(err) {
fmt.Println("目录已存在")
return
}
//MkdirAll使用指定的权限和名称创建一个目录,包括任何必要的上级目录,并返回nil,否则返回错误。
//权限位perm会应用在每一个被本函数创建的目录上。如果path指定了一个已经存在的目录,MkdirAll不做任何操作并返回nil。
err = os.MkdirAll("./foo/bar", 0755)
if err != nil {
fmt.Printf("%v\n", err)
return
}
}
示例14:删除文件
package main
import (
"fmt"
"os"
)
func main() {
//Remove删除name指定的文件或目录。如果出错,会返回*PathError底层类型的错误。
//该方法不能删除非空目录,如果想删除目录以及目录下的所有文件,可以使用RemoveAll
err := os.Remove("./def")
if os.IsNotExist(err) {
fmt.Println("您要删除的文件或目录不存在")
return
}
if err != nil {
fmt.Println(err)
}
//RemoveAll删除path指定的文件,或目录及它包含的任何下级对象。
//它会尝试删除所有东西,除非遇到错误并返回。
//如果path指定的对象不存在,RemoveAll会返回nil而不返回错误。
err = os.RemoveAll("./def")
if err != nil {
fmt.Println(err)
}
}