在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
1.1 os.Fileos.File封装所有文件相关操作, 是一个自定义的struct。 a. 打开一个文件进行读操作: os.Open(name string) (*File, error) Open是以读的方式打开文件(底层调用的是Openfile函数)。 b. 关闭一个文件: File.Close() 1.2 文件操作简单实例示例1: package main import ( "fmt" "io" //读取文件末尾特殊情况需要用到 "os" //读取文件需要用到 ) func main() { filename := "c:/tmp.txt" file, err := os.Open(filename) //返回2个参数,第一个file指针,第二个错误值 if err != nil { //如果打开文件有错误,在这里输出 fmt.Printf("open %s failed,err:%v\n", filename, err) return } defer func() { //打开一个文件,最后我们必须要将其关闭 file.Close() }() var content []byte //定义1个变量存读取到的所有数据 var buf [4096]byte //定义一个4k的字节数组,每次读取一点,4k读性能高 for { n, err := file.Read(buf[:]) //将整个数组转换成切片读进去,Read函数返回2个参数,第1个n是读到的字节数,第二个是err if err != nil && err != io.EOF { //有一个特殊问题,当一个文件读读完,遇到文件末尾时,它也会返回一个错误,但是此时我已经读到文件末尾EOF,这个错误应该不算错误,所以应该把读到文件末尾这个错误给去掉。 fmt.Printf("read %s failed, err:%v\n", filename, err) return //如果有错误就返回 } if err == io.EOF { break //如果读取到文件末尾了,直接break退出。 } vaildBuf := buf[0:n] //把有效数据都拿出来,不可能整个buf数组都是有效数据(最后一次读取到是很大可能是占据数组的一部分。),这里我们就需要借助切片。 //fmt.Printf("%s\n", string(vaildBuf)) content = append(content, vaildBuf...) //将有效的数据存到定义的变量切片中,另外将一个切片append到另一个切片中用... } fmt.Printf("content:%s\n", content) } 执行结果:
示例2: 通过函数传参 package main import ( "fmt" "io" "os" ) func Read(filename string) (string, error) { //获得一个file f, err := os.Open(filename) if err != nil { fmt.Println("read fail") return "", err } //把file读取到缓冲区中 defer f.Close() var content []byte var buf [1024]byte for { //从file读取到buf中, n表示本次读到的字节数 n, err := f.Read(buf[:]) if err != nil && err != io.EOF { fmt.Println("read buf fail", err) return "", err } //说明读取结束 if err == io.EOF { break } //读取到最终的缓冲区中 content = append(content, buf[:n]...) } fmt.Printf("content:%s\n", content) return string(content), nil } func main() { a := "c:/tmp.txt" Read(a) } 执行结果: 1.3 使用bufio提高文件读取性能之前我们是直接从文件读,因为读取文件是一个相对比较慢的操作,所以当我们读一个大的文件时,或者又多次io操作时会影响性能,所以我们使用bufio(缓存机制,跟redis(缓存)有点类似,可以把文件操作类比成数据库操作)提高文件读取性能。 bufio包go语言已经帮我们封装好了。 bufio包总结如下: 1)bufio 包实现了带缓存的 I/O 操作 2)它封装一个 io.Reader 或 io.Writer 对象 3)使其具有缓存和一些文本读写功能 参考网址:https://blog.csdn.net/wangshubo1989/article/details/70177928
代码示例1: package main import ( "bufio" "fmt" "io" "os" ) func main() { filename := "c:/tmp.txt" file, err := os.Open(filename) //返回2个参数,第一个file指针,第二个错误值 if err != nil { //如果打开文件有错误,在这里输出 fmt.Printf("open %s failed,err:%v\n", filename, err) return } defer func() { //打开一个文件,最后我们必须要将其关闭 file.Close() }() reader := bufio.NewReader(file) //要封装这个文件读写,需要把文件传到bufio里面去,然后在bufio里面操作这个缓存,这里我们传的是file文件对象 //为什么要传一个文件对象进去? 答:首先看缓存里有么有,有就读缓存,没有的话就用文件去读,也就是file这个文件对象(是一个结构体,里面包含很多用法) var content []byte //定义1个变量存读取到的所有数据 var buf [4096]byte //定义一个4k的字节数组,每次读取一点,4k读性能高 for { n, err := reader.Read(buf[:]) //将整个数组转换成切片读进去,Read函数返回2个参数,第1个n是读到的字节数,第二个是err if err != nil && err != io.EOF { //有一个特殊问题,当一个文件读读完,遇到文件末尾时,它也会返回一个错误,但是此时我已经读到文件末尾EOF,这个错误应该不算错误,所以应该把读到文件末尾这个错误给去掉。 fmt.Printf("read %s failed, err:%v\n", filename, err) return //如果有错误就返回 } if err == io.EOF { break //如果读取到文件末尾了,直接break退出。 } vaildBuf := buf[0:n] //把有效数据都拿出来,不可能整个buf数组都是有效数据(最后一次读取到是很大可能是占据数组的一部分。),这里我们就需要借助切片。 //fmt.Printf("%s\n", string(vaildBuf)) content = append(content, vaildBuf...) //将有效的数据存到定义的变量切片中,另外将一个切片append到另一个切片中用... } fmt.Printf("content:%s\n", content) } 执行结果:
代码示例2:函数传参 package main import ( "bufio" "fmt" "io" "os" ) func Read(filename string) (string, error) { fi, err := os.Open(filename) if err != nil { return "", err } defer fi.Close() r := bufio.NewReader(fi) var content []byte var buf [1024]byte for { n, err := r.Read(buf[:]) if err != nil && err != io.EOF { return "", err } if err == io.EOF { break } content = append(content, buf[:n]...) } fmt.Printf("content:%s\n", content) return string(content), nil } func main() { a := "c:/tmp.txt" Read(a) } 执行结果: 1.4 使用ioutil读取整个文件针对小文件读取,十分方便 代码示例: package main import ( "fmt" "io/ioutil" ) func main() { filename := "c:/tmp.txt" content, err := ioutil.ReadFile(filename) if err != nil { fmt.Printf("read file %s failed, err:%v\n", filename, err) return } fmt.Printf("content:%s\n", string(content)) //因为上面返回的是一个字节数组,所以必须转一下 } 执行结果:
代码示例2:使用函数传参 package main import ( "fmt" "io/ioutil" ) func Read(filename string) (string, error) { content, err := ioutil.ReadFile(filename) if err != nil { return "", err } fmt.Printf("content:%s\n", content) return string(content), nil } func main() { a := "c:/tmp.txt" Read(a) } 执行结果: 1.5 读取压缩文件示例gizp就相当于read,gzip使用传进去的文件对象去读取文件内容,解压完了在将数据返回。 下面我们通过几个例子来验证一系列理论 1.5.1 bufio读取压缩文件结果package main import ( "bufio" "fmt" "io" "os" ) func main() { fmt.Printf("start run...\n") filename := "c:/tmp.txt.gz" file, err := os.Open(filename) if err != nil { fmt.Printf("open %s failed, err:%v\n", filename, err) return } fmt.Printf("start0 read file\n") defer file.Close() reader := bufio.NewReader(file) var content []byte var buf [4096]byte for { n, err := reader.Read(buf[:]) if err != nil && err != io.EOF { fmt.Printf("read %s failed, err:%v\n", filename, err) return } if err == io.EOF { break } vaildBuf := buf[0:n] content = append(content, vaildBuf...) } fmt.Printf("content:%s\n", content) } 执行结果: 解释: 可以看到用bufio读取到的是压缩文件的二进制代码,也是乱码的。 1.5.2 读取压缩文件出现问题这个问题目前只有读取压缩文件的实例存在,直接读取、bufio、ioutil都不存在这个问题,下面我们通过实例来演示一下。 package main import ( _ "bufio" "compress/gzip" //gzip要用到 "fmt" "io" "os" ) func main() { fmt.Printf("start run...\n") filename := "c:/tmp.txt.gz" file, err := os.Open(filename) if err != nil { fmt.Printf("open %s failed, err:%v\n", filename, err) return } fmt.Printf("start0 read file\n") defer file.Close() /* defer func() { file.Close() }() */ reader, err := gzip.NewReader(file) //用gzip构建文件对象,reader就变成了一个读对象 if err != nil { fmt.Printf("gzip read failed, err:%v\n", err) return } var content []byte var buf [100]byte for { //reader.Read fmt.Printf("start read file\n") n, err := reader.Read(buf[:]) fmt.Printf("read %d err:%v\n", n, err) if err != nil && err != io.EOF { fmt.Printf("read %s failed, err:%v\n", filename, err) return } //读到文件末尾了,文件已经读取完毕,Read方法会返回一个io.EOF错误。 if err == io.EOF { break } validBuf := buf[0:n] content = append(content, validBuf...) } fmt.Printf("content:%s\n", content) } 执行结果: 解释: 可以发现出现了一个严重问题就是当我们每次读取100字节,分多次读取,会出现最后一次读取不到100字节(是52字节),但是err确是EOF,这个时候按照我们上面代码就会break退出,而此次读取到的52字节也会随之丢失,最终实际的结果也是造成了数据丢失。 1.5.3 读取压缩文件完整代码(解决文件丢失问题)package main import ( _ "bufio" "compress/gzip" //gzip要用到 "fmt" "io" "os" ) func main() { fmt.Printf("start run...\n") filename := "c:/tmp.txt.gz" file, err := os.Open(filename) if err != nil { fmt.Printf("open %s failed, err:%v\n", filename, err) return } fmt.Printf("start0 read file\n") defer file.Close() /* defer func() { file.Close() }() */ reader, err := gzip.NewReader(file) //用gzip构建文件对象,reader就变成了一个读对象 if err != nil { fmt.Printf("gzip read failed, err:%v\n", err) return } //reader := bufio.NewReader(file) var content []byte var buf [100]byte for { //reader.Read fmt.Printf("start read file\n") n, err := reader.Read(buf[:]) fmt.Printf("read %d err:%v\n", n, err) if err != nil && err != io.EOF { fmt.Printf("read %s failed, err:%v\n", filename, err) return } if n > 0 { //做一个判断,只要有字节就追加到储存读取到内容的切片 validBuf := buf[0:n] content = append(content, validBuf...) } //读到文件末尾了,文件已经读取完毕,Read方法会返回一个io.EOF错误。 if err == io.EOF { break } } fmt.Printf("content:%s\n", content) } 执行结果: 因输出结果太长,此处截图只截取部分内容。 解释: 通过对读取的字节数进行判断,避免了数据丢失的问题。 1.5.4 补充下面实例有部分用法可能会有疑问,但是写法比较标准,可以学习借鉴。 package main import ( "bufio" "compress/gzip" "fmt" "os" ) func main() { fName := "c:/tmp.txt.gz" var r *bufio.Reader fi, err := os.Open(fName) if err != nil { fmt.Fprintf(os.Stderr, "%v, Can’t open %s: error: %s\n", os.Args[0], fName, err) os.Exit(1) } fz, err := gzip.NewReader(fi) if err != nil { fmt.Fprintf(os.Stderr, "open gzip failed, err: %v\n", err) return } r = bufio.NewReader(fz) for { line, err := r.ReadString('\n') if err != nil { fmt.Println("Done reading file") os.Exit(0) } fmt.Println(line) } } 执行结果:
解释: 先用gizp解压,再用bufio缓存 1.6 文件写入1) os.OpenFile("filename",os.O_WRONLY|os.O_CREATE,066) 第一个参数是文件名 第二个参数是文件的打开模式: os.O_WRONLY:只写 os.O_CREATE:创建文件 os.O_RDONLY:只读 os.O_RDWR:读写 os.O_TRUNC:清空 os.O_APPEND:文件存在以追加方式写入
第三个参数:权限控制 r-->4 w-->2 x-->1 2) os.Create("output.dat") 用途:如果文件不存在需要先创建文件,用到的就是os.Create方法。(如果要创建的这个文件本来就存在,还要用Create(底层调用的Openfile函数)方法创建该文件,那么该文件就会被清空) 1.7 文件写入实例示例1: package main import ( "fmt" "os" ) func isFileExists(filename string) bool { //判断文件是否存在 _, err := os.Stat(filename) //os.Stat会返回文件是否存在的相关属性 if os.IsNotExist(err) { return false } return true } func main() { filename := "c:/tmp.txt" var file *os.File var err error if isFileExists(filename) { file, err = os.OpenFile(filename, os.O_APPEND, 0755) //如果文件存在则追加进去 //file, err = os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0755) //mac系统追加时还需要在加一个os.O_WRONLY方法 } else { file, err = os.Create(filename) //不存在就创建该文件 } if err != nil { fmt.Printf("open %s failed, err:%v\n", filename, err) return } defer file.Close() //给文件中写入内容 n, err := file.WriteString("hello world") //WriteString可以传入字符串 //io.WriteString(file,"hello world") //io.WriteString也可以用来进行传入 if err != nil { fmt.Printf("write failed, err:%v\n", err) return } fmt.Printf("write %d succ\n", n) } 执行结果: 查看文件内容:可以发现新追加的也添加进去了。
示例2: package main import ( "io" "os" ) func CheckFileExist(fileName string) bool { _, err := os.Stat(fileName) if os.IsNotExist(err) { return false } return true } func Write(fileName string) error { var f *os.File var err error if CheckFileExist(fileName) { //文件存在 f, err = os.OpenFile(fileName, os.O_APPEND, 0666) //打开文件 if err != nil { return err } } else { //文件不存在 f, err = os.Create(fileName) //创建文件 if err != nil { return err } } _, err = io.WriteString(f, "strTest") if err != nil { return err } return nil } func main() { Write("c:/tmp.txt") } 执行结果:
查看文件内容:
示例3:Fprintf fmt包的Printf底层调用的就是Fprintf 实例如下: package main import ( "fmt" "os" ) func isFileExists(filename string) bool { _, err := os.Stat(filename) if os.IsNotExist(err) { return false } return true } func main() { filename := "c:/tmp.txt" var file *os.File var err error if isFileExists(filename) { //mac机器 file, err = os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0755) } else { file, err = os.Create(filename) } if err != nil { fmt.Printf("open %s failed, err:%v\n", filename, err) return } defer file.Close() fmt.Fprintf(file, "%d %d is good", 100, 300) //这里主要是为了演示一下Printf的底层调用Fprintf,我们可以通过传入文件对象,将其写入文件 } 执行结果如下:
查看文件内容:
1.8 使用ioutil直接文件写入ioutil相当于每次把要写入的内容以覆盖形式写入到文件内 实例1: package main import ( "fmt" "io/ioutil" ) func main() { filename := "c:/tmp.txt" str := "dkfslfjdsklfjlskjflsjflsjflsjflks" err := ioutil.WriteFile(filename, []byte(str), 0755) if err != nil { fmt.Println("write fail") } fmt.Println("write success") } 执行结果: 查看文件内容:
实例2: package main import ( "fmt" "io/ioutil" ) func Write() { fileName := "c:/tmp.txt" strTest := "测试测试" var d = []byte(strTest) err := ioutil.WriteFile(fileName, d, 0666) if err != nil { fmt.Println("write fail") } fmt.Println("write success") } func main() { Write() } 执行结果:
查看文件内容:
1.9 使用bufio进行文件写入bufio会减少写入文件的次数,先写到缓存中,然后会调用Flush方法将内存中的内容刷新到磁盘中。 bufio写入的缺点: 会造成文件丢失(当要写入的内容还在缓存中,还未被写入到磁盘时,程序挂了,那这些数据就丢失了。) 示例1: package main import ( "bufio" "fmt" "os" ) func isFileExists(filename string) bool { _, err := os.Stat(filename) if os.IsNotExist(err) { return false } return true } func main() { filename := "c:/tmp.txt" var file *os.File var err error file, err = os.Create(filename) if err != nil { fmt.Printf("open %s failed, err:%v\n", filename, err) return } defer file.Close() writer := bufio.NewWriter(file) writer.WriteString("hello worldldfdsfsfsf") writer.Flush() } 执行结果: 查看文件内容:
示例2: package main import ( "bufio" "fmt" "os" ) func Write() { fileName := "c:/tmp.txt" f, err3 := os.Create(fileName) //创建文件 if err3 != nil { fmt.Println("create file fail") } w := bufio.NewWriter(f) //创建新的 Writer 对象 n4, err3 := w.WriteString("bufferedn") //此时还是写入在内存中 fmt.Printf("写入 %d 个字节\n 全部评论
专题导读
热门推荐
热门话题
阅读排行榜
|
请发表评论