对于文件,咱们并不陌生,文件是数据源(保存数据的地方)的一种,好比你们常用的word文档,txt文件,Excel文件...等等都是文件。文件最主要的做用就是保存数据,它既能够保存一张图片,也能够保存视频,声音......
文件在程序中是以流的形式来操做的。golang
流:数据在数据源(文件)和程序(内存)之间经历的路径
输出流:数据从程序(内存)到数据源(文件)的路径
输入流:数据从数据源(文件)到程序(内存)的路径
输入与输出都是相对于内存而言的,从内存向外流就是输出,从外部向内存流就是输入缓存
在Go中,咱们操做文件的方法在os包中,会常用到os.File结构体 Go语言标准库文档app
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) } }() }
使用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)) }
读取文件内容并显示在终端,将文件内容一次性读取到终端,适用于文件不大的状况。测试
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)) }
读取文件的内容并显示在终端(带缓冲区的方式),使用os.Open
, file.Close
,bufio.NewReader
,reader.ReadString
函数和方法。适合读取大文件
1.使用ReadBytes方法
代码1:3d
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方法code
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) }
第二个参数:文件代开模式(能够组合);第三个参数:权限控制(如0755)
orm
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) } }
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() }
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) } }
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) }
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 }
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") }
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) }
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) } }
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 } }
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) } }