I/O
官网课程
购买课程找博主推荐
文章目录
- I/O
- 文件信息
- 创建文件、目录
- IO读
- IO写(权限)
- 文件复制
- Seeker接口
- 断点续传
- 遍历文件夹
- bufio
电脑中一切,都是以 二进制流的形式存在的。
jpg:010100000010010101001010101010010101010 编码格式,还原为一个图片展示到计算机显示器
mp4:010100000010010101001010101010010101010
i:input 输入
o:output 输出
文件信息
文本文件
二进制文件
file 类是封装 os 包中的。
封装了文件的信息:FileInfo 、 Read、Write
1、读取文件信息 FileInfo
2、向文件中写入内容 Write
3、读取文件内容 Read
package main
import (
"fmt"
"os"
)
// file
// fileInfo
/*
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()
// 获取更加详细的文件信息, *syscall.Stat_t 反射来获取
Sys() any // underlying data source (can return nil)
*/
func main() {
// 获取某个文件的状态
fileinfo, err := os.Stat("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\test")
if err != nil {
return
}
fmt.Println(fileinfo.Name()) // demo01.go
fmt.Println(fileinfo.IsDir()) // false
fmt.Println(fileinfo.ModTime()) // 2023-02-23 20:25:43.1772351 +0800 CST
fmt.Println(fileinfo.Size()) // 1186 字节数
fmt.Println(fileinfo.Mode()) // -rw-rw-rw-
}
创建文件、目录
通过代码创建文件
路径:
- 相对路径
- 相对当前目录的路径
- ./ 当前目录
- …/ 上一级目录
- 绝对路径
- 从盘符开始的路径
创建目录
mkdir / 权限
mkdirAll 创建层级目录
remove 删除目录
removeAll 强制删除目录
package main
import (
"fmt"
"os"
)
// 创建目录
// 项目开源框架,一运行,就会自动生成脚手架目录
func main() {
// 打开一个文件夹(1、存在我就打开 2、不存在,创建这个文件夹)
// func Mkdir(name string, perm FileMode) error
// ModePerm : 0777
err := os.Mkdir("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\file1", os.ModePerm)
if err != nil {
// 存在就无法创建了 Cannot create a file when that file already exists.
fmt.Println(err)
}
fmt.Println("文件夹创建完毕")
// 创建层级文件夹
err2 := os.MkdirAll("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\file2\\aa\\bb\\cc\\dd", os.ModePerm)
if err2 != nil {
fmt.Println(err2)
}
fmt.Println("层级文件夹创建完毕")
// 删除 remove
// func Remove(name string) error
// 通过remove方法只能删除单个空文件夹:
// remove D:\Environment\GoWorks\src\xuego\lesson11\file2: The directory is not empty.
err3 := os.Remove("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\file2")
if err3 != nil {
fmt.Println(err3)
//return
}
fmt.Println("file delete success!!")
// 如果存在多层文件,removeAll,相对来说比较危险,删除这个目录下的所有东西, 强制删除
err4 := os.RemoveAll("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\file2")
if err4 != nil {
fmt.Println(err4)
return
}
fmt.Println("err4 delete success!!")
}
创建文件
os.create(),若存在就是的打开-就是返回的这个file对象. 如果不存在,创建在打开
package main
import (
"fmt"
"os"
)
func main() {
// 创建文件 Create
// func Create(name string) (*File, error) {
// 返回的file对象就是我们的文件
file1, err := os.Create("a.go") // 相对路径
if err != nil {
fmt.Println(err)
}
fmt.Println(file1)
// 删除
os.Remove("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\a.go")
}
IO读
1、与文件建立连接
package main
import (
"fmt"
"os"
)
// IO读
func main() {
// 找到这个文件的对象 create 创建、 打开Open
// func Open(name string) (*File, error)
file1, err := os.Open("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\123.txt")
if err != nil {
fmt.Println(err)
}
fmt.Println(file1)
// file 类-- 指定的对象
// 打开文件的时候,选定权限: 可读可写的方式打开
// OpenFile(文件名,打开方式:可读、可写...,FileMode , 权限)
file2, err2 := os.OpenFile("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\123.txt",
os.O_RDONLY|os.O_WRONLY, os.ModePerm)
if err2 != nil {
fmt.Println(err2)
}
fmt.Println(file2)
// 可以操作这个对象了
}
2、读取 file.Read([]byte) ,将file中的数据读取到 []byte 中, n,err n读取到的行数,err 错误,EOF错误,就代表文件读取完毕了
一直调用read,就代表光标往后移动…
package main
import (
"fmt"
"os"
)
// 读取文件数据
func main() {
// 我们习惯于在建立连接时候通过defer来关闭连接,保证程序不会出任何问题,或者忘记关闭
// 建立连接
file, _ := os.Open("123.txt")
// 关闭连接
defer file.Close()
// 读代码 ,Go 的错误机制,让我们专心可以写业务代码。
// 1、创建一个容器 (二进制文本文件--0100101010 => 读取流到一个容器 => 读取容器的数据)
bs := make([]byte, 2, 1024) // 缓冲区,可以接受我们读取的数据
// 2、读取到缓冲区中。 // 汉字一个汉字占 3个位置
n, err := file.Read(bs)
fmt.Println(n)
fmt.Println(err)
fmt.Println(string(bs)) // 读取到的字符串 ab
// 光标不停的向下去指向,读取出来的内容就存到我们的容器中。
file.Read(bs)
fmt.Println(string(bs)) // 读取到的字符串 cd
file.Read(bs)
fmt.Println(string(bs)) // 读取到的字符串 e
n, err = file.Read(bs)
fmt.Println(n)
fmt.Println(err) // EOF ,读取到了文件末尾。就会返回EOF。
fmt.Println(string(bs)) // 读取到的字符串
n, err = file.Read(bs)
fmt.Println(n)
fmt.Println(err)
fmt.Println(string(bs)) // 读取到的字符串
}
IO写(权限)
建立连接 (设置权限:可读可写,扩充这个文件的append) os.OpenFile
关闭连接
写入 file.write
- file.Write
- file.WriteString
package main
import (
"fmt"
"os"
)
func main() {
fileName := "123.txt"
// 权限:如果我们要向一个文件中追加内容, O_APPEND, 如果没有,就是从头开始写
file, _ := os.OpenFile(fileName, os.O_WRONLY|os.O_RDONLY|os.O_APPEND, os.ModePerm)
defer file.Close()
// 操作
bs := []byte{65, 66, 67, 68, 69} // A B C D E
n, err := file.Write(bs)
if err != nil {
fmt.Println(err)
}
fmt.Println(n)
// string类型的写入
n, err = file.WriteString("hhahahahah哈哈哈哈哈哈哈")
if err != nil {
fmt.Println(err)
}
fmt.Println(n)
}
文件复制
package utils
import (
"fmt"
"io"
"os"
)
// Copy 方法需要参数为:source 源文件 ,destination 目标文件
func Copy(source, destination string, bufferSize int) {
// 读取文件
sourceFile, err := os.Open(source)
if err != nil {
fmt.Println("Open错误:", err)
}
// 输出文件 O_WRONLY , O_CREATE 如果不不存在,则会创建
destinationFile, err := os.OpenFile(destination, os.O_WRONLY|os.O_CREATE, os.ModePerm)
if err != nil {
fmt.Println("OpenFile错误:", err)
}
// 关闭
defer sourceFile.Close()
defer destinationFile.Close()
// 专注业务代码,拷贝
buf := make([]byte, bufferSize)
// 读取
for {
n, err := sourceFile.Read(buf)
if n == 0 || err == io.EOF {
fmt.Println("读取完毕源文件,复制完毕")
break
} else if err != nil {
fmt.Println("读取错误:", err)
return // 错误之后,必须要return终止函数执行。
}
// 将缓冲区的东西写出到目标文件
_, err = destinationFile.Write(buf[:n])
if err != nil {
fmt.Println("写出错误:", err)
}
}
}
调用
package main
import "xuego/lesson11/utils"
func main() {
source := "C:\\xq.png"
dest := "D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\xq.png"
utils.Copy(source, dest, 1024)
}
系统给我们提供了copy方法
// 调用系统的方法
func Copy2(source, destination string) {
// 读取文件
sourceFile, err := os.Open(source)
if err != nil {
fmt.Println("Open错误:", err)
}
// 输出文件 O_WRONLY , O_CREATE 如果不不存在,则会创建
destinationFile, err := os.OpenFile(destination, os.O_WRONLY|os.O_CREATE, os.ModePerm)
if err != nil {
fmt.Println("OpenFile错误:", err)
}
// 关闭
defer sourceFile.Close()
defer destinationFile.Close()
// 具体的实现
written, err := io.Copy(destinationFile, sourceFile)
fmt.Println("文件的字节大小:", written)
}
第三方包,也提供了这些方法。
Seeker接口
设置光标的位置,读写文件
type Seeker interface {
// 1、offset 偏移量 3
// 2、whence 如何设置,当前光标的位置。
Seek(offset int64, whence int) (int64, error)
}
// 如何让光标在第三个位置?
// 1、找到当前光标在哪里
// a) 文件的头部, 0
// b) 文件的尾部 end
// c) 在任意地方,相对位置。
const (
SeekStart = 0 // 表示相对于文件的开头
SeekCurrent = 1 // 表示相对于当前光标所在的位置
SeekEnd = 2 // 相对于文件的末尾
)
关于seek的使用
package main
import (
"fmt"
"io"
"os"
)
func main() {
// 读取文件
file, _ := os.OpenFile("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\a.txt",
os.O_RDWR, os.ModePerm)
// defer close
defer file.Close()
// 测试seek
// 相对开始位置。io.SeekStart
// 相对于文件末尾, io.SeekEnd
file.Seek(2, io.SeekStart)
buf := []byte{0}
file.Read(buf)
fmt.Println(string(buf))
// 相对于当前位置
file.Seek(3, io.SeekCurrent)
file.Read(buf)
fmt.Println(string(buf))
// 在结尾追加内容
file.Seek(0, io.SeekEnd)
file.WriteString("hahahaha")
}
断点续传
思考几个问题:
1、如果你要传的文件很大,70G,是否有方法可以缩短耗时?
- 将文件拆分
- 同时多线程进行下载
2、如果在文件传递过程中,程序被迫中断(断电、断网、内存满了…),下次重启之后,文件是否还需要重头再传?
- 希望能够继续上传或者下载
3、传递文件的时候,支持暂停和恢复上传?假设这个两个操作分布在重启前后?
- 支持!
file、read、write、seek
思路:
1、需要记住上一次传递了多少数据、temp.txt => 记录
2、如果被暂停或者中断了,我们就可以读取这个temp.txt的记录,恢复上传
3、删除temp.txt
理解
package main
import (
"fmt"
"io"
"os"
"strconv"
)
// 断点续传
func main() {
// 传输源文件地址
srcFile := "C:\\Users\\遇见狂神说\\Desktop\\client\\gp.png"
// 传输的目标位置
destFile := "D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\server\\gp-upload.png"
// 临时记录文件
tempFile := "D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\temp.txt"
// 创建对应的file对象,连接起来
file1, _ := os.Open(srcFile)
file2, _ := os.OpenFile(destFile, os.O_CREATE|os.O_RDWR, os.ModePerm)
file3, _ := os.OpenFile(tempFile, os.O_CREATE|os.O_RDWR, os.ModePerm)
defer file1.Close()
defer file2.Close()
fmt.Println("file1/2/3 文件连接建立完毕")
// 1、读取temp.txt
file3.Seek(0, io.SeekStart)
buf := make([]byte, 1024, 1024)
n, _ := file3.Read(buf)
// 2、转换成string - 数字。
countStr := string(buf[:n])
count, _ := strconv.ParseInt(countStr, 10, 64)
fmt.Println("temp.txt中记录的值为:", count) // 5120
// 3、设置读写的偏移量
file1.Seek(count, io.SeekStart)
file2.Seek(count, io.SeekStart)
fmt.Println("file1/2 光标已经移动到了目标位置")
// 4、开始读写(复制、上传)
bufData := make([]byte, 1024, 1024)
// 5、需要记录读取了多少个字节
total := int(count)
for {
// 读取数据
readNum, err := file1.Read(bufData)
if err == io.EOF { // file1 读取完毕了
fmt.Println("文件传输完毕了")
file3.Close()
os.Remove(tempFile)
break
}
// 向目标文件中写入数据
writeNum, err := file2.Write(bufData[:readNum])
// 将写入数据放到 total中, 在这里total 就是传输的进度
total = total + writeNum
// temp.txt 存放临时记录数据
file3.Seek(0, io.SeekStart) // 将光标重置到开头
file3.WriteString(strconv.Itoa(total))
}
}
遍历文件夹
package main
import (
"fmt"
"log"
"os"
)
// cd /d 文件夹路径
// tree /F , 查看当前文件夹下的所有文件
// 遍历文件夹
// 1、读取当前文件夹下的所有文件
// 2、如果是文件夹,进入文件夹,继续读取里面的所有文件
// 3、设置一些结构化代码
func main() {
dir := "D:\\Environment\\GoWorks\\src\\xuego"
tree(dir, 0)
}
// 日常调试测试常用fmt输出 、 工作中or项目中更多是log日志输出
func tree(dir string, level int) {
// 编写层级
tabString := "|--"
for i := 0; i < level; i++ {
tabString = "| " + tabString
}
// 获取目录 ReadDir, 返回目录信息[]DirEntry,多个文件信息
fileInfos, err := os.ReadDir(dir)
if err != nil {
log.Println(err)
}
// 遍历出来所有文件之后,获取里面的单个文件
for _, file := range fileInfos {
// 文件夹中文件的全路径展示
filename := dir + "\\" + file.Name()
fmt.Println(tabString + file.Name())
// 如果是文件夹,再次遍历
if file.IsDir() {
tree(filename, level+1)
}
}
}
bufio
Go语言自带的IO操作包。bufio,使用这个包可以大幅提升文件的读写效率。
buf: 缓冲区.
io操作效率本身是还可以的,频繁访问本地磁盘文件(效率低)
所以说 bufio ,提供了一个缓冲区,读和写都先在缓冲区中,最后再一次性读取或者写入到文件里,降低访问本地磁盘的次数。
bufio写入
package main
import (
"bufio"
"fmt"
"log"
"os"
)
// bufio 的应用
func main() {
file, err := os.Open("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\demo01.go")
if err != nil {
log.Println(err)
}
defer file.Close()
// 读取文件
// 创建一个bufio包下的 reader对象。
//bufioReader := bufio.NewReader(file)
//buf := make([]byte, 1024)
//n, err := bufioReader.Read(buf)
//fmt.Println("读取到了多少个字节:", n)
// 读取键盘的输入
// 键盘的输入,实际上是流 os.Stdin
inputReader := bufio.NewReader(os.Stdin)
// delim 到哪里结束读取
readString, _ := inputReader.ReadString('\n')
fmt.Println("读取键盘输入的信息:", readString)
}
bufio写出
package main
import (
"bufio"
"fmt"
"os"
)
// 写入
func main() {
file, _ := os.OpenFile("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\a.txt",
os.O_RDWR|os.O_CREATE,
os.ModePerm)
defer file.Close()
// bufio
fileWrite := bufio.NewWriter(file)
writeNum, _ := fileWrite.WriteString("kuangshen")
fmt.Println("writeNum:", writeNum)
// 发现并没有写出到文件,是留在了缓冲区,所以我们需要调用 flush 刷新缓冲区
// 手动刷新进文件
fileWrite.Flush()
}
i/o差不多就这些,整理狂神笔记,可以到官网购买课程
官网连接