作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
一.文件的读取操作
1.什么是文件
计算机中的文件是存储在外部介质(通常是磁盘)上的数据集合,文件分为文本文件和二进制文件。
文件是保存数据的地方,是数据源的一种,比如大家经常使用的word文件,txt文件,Excel,jpg,png,mp3,mp4,rmvb等都是文件。
文件最主要的做哟合格就是保存数据,它既可以保存一张图片,也可以保存视频,声音等等。
2.IO流类型概述
如上图所示,IO(Input/Output)流是程序和数据源沟通的桥梁,IO流分为输入流和输出流两个不同的方向。
输入流一般指的是程序读取源文件的场景。
输出流一般指的是程序写入数据到目标文件的场景。
3.文件的基本操作
3.1 打开和关闭文件
package main
import (
"fmt"
"os"
)
func main() {
// 1.打开文件
file, err := os.Open("/etc/hosts")
if err != nil {
fmt.Printf("打开文件出错, err= %v\n", err)
}
// 2.在程序退出前一定要关闭文件以释放资源
defer file.Close()
// 3.打开文件若没错,则可以对文件进行一系列的操作
fmt.Printf("文件对象: %v\n", file)
}
3.2 带缓冲大小读取文件内容
package main
import (
"fmt"
"io"
"os"
)
func main() {
// 只读方式打开当前目录下的hosts文件
file, err := os.Open("/etc/hosts")
if err != nil {
fmt.Println("打开文件失败!, err = ", err)
return
}
// 为了防止文件忘记关闭导致内存泄漏,我们通常使用defer注册文件关闭语句。
defer file.Close()
// 定义一个缓冲区的字节切片,此处咱们可以一次性读取4096个字节大小数据区域。
var cache = make([]byte, 4096)
/*
使用Read方法读取数据,需要专递一个字节切片,该切片大小表示一次性读取的文件内容的大小。
- 1.当我们传递的字节切片大小较小时,尽管没有读取完文件的所有内容,也不会读取超出的内容哟。
- 2、当我们传递的字节切片大小较大时,会将文件的内容全部读取出来,并不会重复读取文件的内容哟~
*/
n, err := file.Read(cache)
if err == io.EOF {
fmt.Println("文件读完了")
return
}
if err != nil {
fmt.Println("read file failed, err:", err)
return
}
fmt.Printf("读取了%d字节数据\n", n)
fmt.Printf("文件内容:\n%s", string(cache[:n]))
}
3.3 循环读取
package main
import (
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("/etc/hosts")
if err != nil {
fmt.Println("open file failed!, err:", err)
return
}
defer file.Close()
var content []byte
var tmp = make([]byte, 128)
// 使用for循环读取文件中的所有数据。
for {
// 一次性读取的128字节的大小
buf, err := file.Read(tmp)
if err == io.EOF {
fmt.Println("文件读完了")
break
}
if err != nil {
fmt.Println("文件读取失败,错误:", err)
return
}
// 每次读取buf大小后都追加到content切片中
content = append(content, tmp[:buf]...)
}
// 将所有的内容读取到content切片后,再统一打印
fmt.Printf("文件内容:\n%s", string(content))
}
4.ioutil包一次性读取小文件
4.1 ioutil包概述
读取文件的内容并显示在终端(使用ioutil一次性将整个文件读入到内存中),这种方式适用于文件不大的情况。
相关方法的函数有"ioutil.ReadFile"。
4.2 ioutil读取整个文件
package main
import (
"fmt"
"io/ioutil"
)
func main() {
// "io/ioutil"包的ReadFile方法能够读取完整的文件,只需要将文件名作为参数传入。
content, err := ioutil.ReadFile("/etc/hosts")
if err != nil {
fmt.Println("读取文件出错, err:", err)
return
}
// 读取文件的内容并显示在终端(使用ioutil一次性将整个文件读入到内存中),这种方式适用于文件不大的情况。
fmt.Printf("文件内容: \n%v", string(content))
}
5.bufio包按行读取文件
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
// 打开文件
file, err := os.Open("/etc/hosts")
if err != nil {
fmt.Println("文件打开失败, err=", err)
return
}
// 当函数退出是,让file关闭,防止内存泄漏;
defer file.Close()
// 创建一个IO流,bufio是在file的基础上封装了一层API,支持更多的功能。
reader := bufio.NewReader(file)
// 读取操作
for {
// 按行读取,注意是字符'\n'。
line, err := reader.ReadString('\n')
// 表示已经读取到文件的结尾
if err == io.EOF {
if len(line) != 0 {
fmt.Println(line)
}
fmt.Println("文件读完了")
break
}
if err != nil {
fmt.Println("读取文件失败, err:", err)
return
}
// 如果没有读取到文件结尾的话,就正常输出文件内容即可。
fmt.Print(line)
}
//
fmt.Println("文件读取成功,程序运行结束...")
}
二.文件的写入操作
1.文件的打开模式
模式 | 含义 |
---|---|
os.O_RDONLY |
只读模式打开文件 |
os.O_WRONLY |
只写模式打开文件 |
os.O_RDWR |
读写模式打开文件 |
os.O_APPEND |
写操作时将数据附加到文件尾部 |
os.O_CREATE |
创建文件 |
os.EXCL |
和os.OCREATE 配合使用,文件必须不存在 |
os.SYNC |
打开文件用于同步I/O |
os.O_TRUNC |
如果文件存在,则打开文件时先清空再打开 |
os.OpenFile()函数能够以指定模式打开文件,从而实现文件写入相关功能。
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
...
}
相关参数说明:
- name:
要打开的文件名
- flag:
打开文件的模式,常见的模式如上表所示。
- perm:
文件权限,一个八进制数。r(读)04,w(写)02,x(执行)01。
2.Write和WriteString
package main
import (
"fmt"
"os"
)
func main() {
// 以只写的模式打开文件
file, err := os.OpenFile("blog.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil {
fmt.Println("文件打开失败, err= ", err)
return
}
defer file.Close()
str := "博客地址: https://www.cnblogs.com/yinzhengjie\n\n"
//写入字节切片数据
file.Write([]byte(str))
//直接写入字符串数据
file.WriteString("Go视频教学地址: https://www.bilibili.com/video/BV1bwhve7EPJ/\n")
}
3.bufio.NewWriter写入文件
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
// 以只写的模式打开文件
file, err := os.OpenFile("./yinzhengjie.log", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
// 判断文件是否打开失败
if err != nil {
fmt.Println("open file failed, err:", err)
return
}
// 及时将文件关闭,防止内存泄漏
defer file.Close()
// 写入操作,获取一个IO流,带缓冲区的输出流
writer := bufio.NewWriter(file)
// 将数据写入缓冲区
for i := 0; i < 10; i++ {
writer.WriteString("博客地址: https://www.cnblogs.com/yinzhengjie\n")
}
// 将缓冲区的数据刷新到磁盘
writer.Flush()
// tips: 查看文件打开模式,需要懂Linux基础知识
mode := os.FileMode(0644).String()
fmt.Printf("mode: 0644 = %s\n", mode)
}
4.WriteFile
package main
import (
"fmt"
"io/ioutil"
)
func main() {
str := "{\"尹正杰博客地址\": \"https://www.cnblogs.com/yinzhengjie\"}"
// ioutil.WriteFile写入文件时,若文件已存在或者有内容,会直接将源文件清空哟~
err := ioutil.WriteFile("./blog.json", []byte(str), 0666)
if err != nil {
fmt.Println("文件打开失败,错误 = ", err)
return
}
}
三.文件的复制操作
1.拷贝文件内容案例
package main
import (
"fmt"
"io/ioutil"
)
func main() {
// 1.定义需要拷贝的源文件
srcFile := "/etc/hosts"
// 2.定义拷贝到目标文件路径
destFile := "./hosts"
// 3.对源文件进行读取数据
content, err := ioutil.ReadFile(srcFile)
if err != nil {
fmt.Printf("文件读取失败: err = %s\n", err)
return
}
// 4.将读取到的数据写入目标文件
err = ioutil.WriteFile(destFile, content, 0644)
if err != nil {
fmt.Printf("文件写入失败, err=%v\n", err)
}
fmt.Printf("'%s'文件拷贝完成\n", srcFile)
}
2.借助io.Copy()
实现一个拷贝文件函数
package main
import (
"fmt"
"io"
"os"
)
// CopyFile 拷贝文件函数
func CopyFile(dstFile, srcFile string) (written int64, err error) {
// 以读方式打开源文件
src, err := os.Open(srcFile)
if err != nil {
fmt.Printf("打开%s文件失败, err = %v\n", srcFile, err)
return
}
defer src.Close()
// 以写|创建的方式打开目标文件
dest, err := os.OpenFile(dstFile, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
fmt.Printf("打开%s文件失败, err = %v\n", dstFile, err)
return
}
defer dest.Close()
//调用io.Copy()拷贝内容文件内容
return io.Copy(dest, src)
}
func main() {
_, err := CopyFile("./hosts", "/etc/hosts")
if err != nil {
fmt.Println("拷贝文件失败,err = ", err)
return
}
fmt.Println("拷贝完成")
}