IO
IO
获取文件信息
计算机中的文件是存储在外部介质(通常是磁盘)上的数据集合,文件分为文本文件和二进制文件
file类是os包中的,封装了底层的文件描述符和相关信息,同时封装了Read和Write的实现
FileInfo接口中定义了File信息相关的方法
package main
import (
"fmt"
"os"
)
func main() {
fileinfo, err := os.Stat("src/main/10_I_O/a.txt")
if err != nil {
fmt.Println(err)
return
}
//获取文件的名字
fmt.Println(fileinfo.Name())
//获取文件的权限
fmt.Println(fileinfo.Mode())
//判断是否是目录
fmt.Println(fileinfo.IsDir())
//获取文件最后一次修改
fmt.Println(fileinfo.ModTime())
//判断文件的大小是多少字节
fmt.Println(fileinfo.Size())
//反射获取文件更加详细的信息
fmt.Println(fileinfo.Sys())
}
科普 权限
linux下有两种文件权限表示方式,符号和八进制表示
- --- --- --- type owner group others type:文件类型 - 文件 d目录 |连接符号 ---,代表的文件读写可执行 字母 r(读) w(写) x(执行),如果没有那个权限就用 - 代替
八进制表示
r 004 w 002 x 001 - 000 比如 777 就是可读可写可执行
创建目录与文件
创建目录
package main
import (
"fmt"
"os"
)
func main() {
err := os.Mkdir("src/main/10_I_O/test2", os.ModePerm)
if err != nil {
fmt.Println(err)
//return
}
err = os.Mkdir("src/main/10_I_O/test3", os.ModePerm)
//创建层级文件夹 os.MkdirAll()
err = os.MkdirAll("src/main/10_I_O/test3/a/b/c", os.ModePerm)
if err != nil {
fmt.Println(err)
}
fmt.Println("文件夹创建完毕")
err2 := os.Remove("src/main/10_I_O/test2")
if err2 != nil {
fmt.Println(err2)
}
fmt.Println("文件夹删除完毕")
err3 := os.RemoveAll("src/main/10_I_O/test3")
if err3 != nil {
fmt.Println(err3)
}
fmt.Println("文件夹包括子文件删除完毕啦~")
}
创建文件
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Create("src/main/10_I_O/b.txt")
fmt.Println(file.Name())
fmt.Println(err)
}
IO读
打开文件
读取a.txt文件中的数据
package main
import (
"fmt"
"os"
)
func main() {
////1。 打开文件,建立连接
//file, err := os.Open("src/main/10_I_O/a.txt")
//if err != nil {
// fmt.Println(err)
// return
//}
//fmt.Println(file.Name())
//
////2.关闭连接
//defer file.Close()
//1。 打开文件,建立连接
//os.OpenFile(文件路径,文件打开方式,文件权限)
//file, err := os.OpenFile("src/main/10_I_O/b.txt", os.O_RDONLY|os.O_WRONLY, os.ModePerm)
file, err := os.Open("src/main/10_I_O/a.txt")
fileinfo, _ := os.Stat("src/main/10_I_O/a.txt")
fmt.Println(fileinfo.Mode())
if err != nil {
fmt.Println(err)
return
}
fmt.Println(file.Name())
//2.关闭连接
defer file.Close()
//读取a.txt的内容
//Read到容器,放回读取的数量 n
//如果读到了文件的末尾,err = is.EOF
bs := make([]byte, 1024, 1024)
n, err := file.Read(bs)
fmt.Println(n)
fmt.Println(string(bs))
fmt.Println(err)
}
IO写
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.OpenFile("src/main/10_I_O/a.txt", os.O_RDONLY|os.O_WRONLY|os.O_APPEND, os.ModePerm)
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
//业务代码
bs := []byte{65, 66, 67, 68, 69, 90, 123}
n, err := file.Write(bs)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(n)
//字符串的输入
n, err = file.WriteString("hello,我是yama")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(n)
}
文件复制
源文件-读取-程序-写出-目标文件
package main
import (
"fmt"
"io"
"os"
)
func main() {
source := "/Users/zuoliang/Documents/go/src/main/10_I_O/img/go_logo.jpeg"
destination := "/Users/zuoliang/Documents/go/src/main/10_I_O/img-copy/go_logo_copy.jpeg"
//copy(source, destination, 1024)
//copy2(destination, source)
copy3(destination, source)
}
func copy(source, destination string, bufSize int) {
//输入文件
sourceFile, err := os.Open(source)
if err != nil {
fmt.Println(err)
return
}
defer sourceFile.Close()
//输出文件
destinationFile, err := os.OpenFile(destination, os.O_WRONLY|os.O_CREATE, os.ModePerm)
if err != nil {
fmt.Println(err)
return
}
defer destinationFile.Close()
//缓冲区
buf := make([]byte, bufSize)
for {
//读取
n, err := sourceFile.Read(buf)
if err == io.EOF || n == 0 {
fmt.Println("文件复制完毕了")
break
}
//写出
_, err = destinationFile.Write(buf[:n])
if err != nil {
fmt.Println("写出失败", err)
return
}
}
}
func copy2(destination, source string) {
//输入文件
sourceFile, err := os.Open(source)
if err != nil {
fmt.Println(err)
return
}
defer sourceFile.Close()
//输出文件
destinationFile, err := os.OpenFile(destination, os.O_WRONLY|os.O_CREATE, os.ModePerm)
if err != nil {
fmt.Println(err)
return
}
defer destinationFile.Close()
//io
written, _ := io.Copy(destinationFile, sourceFile)
fmt.Println("文件的大小为:", written)
}
func copy3(destination, source string) {
//它是一次性读取文件一次性写出,不适合较大的文件,容易内存益处
fileBuf, _ := os.ReadFile(source)
os.WriteFile(destination, fileBuf, 0777)
}
Seeker接口
Seeker是包装Seek方法的接口
type Seeker interface{
Seek(Offset int64,whence int)(int64, error)
}
seek(offset,whence)设置指针光标的位置,读写文件:
第一个参数:偏移量
第二个参数:如何设置
0,seekStart 表示相对于文件开头
1,seekCurrent 表示相对于当前光标所在位置来说的
2,seekend 表示相对于文件末尾
package main
import (
"fmt"
"io"
"os"
)
func main() {
file, _ := os.OpenFile("/Users/zuoliang/Documents/go/src/main/10_I_O/a.txt", os.O_RDWR, os.ModePerm)
defer file.Close()
//业务
file.Seek(2, io.SeekStart)
buf := []byte{0}
file.Read(buf)
fmt.Println(string(buf))
file.Seek(2, io.SeekCurrent)
file.Read(buf)
fmt.Println(string(buf))
file.Seek(0, io.SeekEnd)
file.WriteString("我是追加数据")
}
断点续传
首先思考几个问题:
- 如果你要传的文件,比较大,那么是否有方法可以缩短耗时?
- 如果在文件传递过程中,程序因各种原因被迫中断了,那么下次再重启时,文件是否还需要重头开始?
- 传递文件的时候,支持暂停和恢复么?即使这两个操作分布在程序被杀前后。
这些都是可以通过Seek()方法实现
思路:想实现断点续传,主要就是记住上一次已经传递了多少数据,那我们可以创建一个临时文件,记录已经传递的数据量,当恢复传递的时候,先从临时文件中读取上次已经传递的数据量,然后通过Seek()方法,设置到该读和该写的位置,再继续传递数据。
package main
import (
"fmt"
"io"
"os"
"strconv"
)
func main() {
//传输源文件
srcFile := "/Users/zuoliang/Documents/go/src/main/10_I_O/img/go_logo.jpeg"
//传输目的地
destFile := "/Users/zuoliang/Documents/go/src/main/10_I_O/seek/seek.jpeg"
//临时记录文件
tempFile := "/Users/zuoliang/Documents/go/src/main/10_I_O/temp.txt"
//与文件建立连接读取
file1, _ := os.Open(srcFile)
file2, _ := os.OpenFile(destFile, os.O_CREATE|os.O_RDWR, 0777)
file3, _ := os.OpenFile(tempFile, os.O_CREATE|os.O_RDWR, os.ModePerm)
defer file1.Close()
defer file2.Close()
//1,读取临时文件记录的数据
file3.Seek(0, io.SeekStart)
buf := make([]byte, 1024, 1024)
n, _ := file3.Read(buf)
countStr := string(buf[:n])
fmt.Println("contStr", countStr)
//seek
count, _ := strconv.ParseInt(countStr, 10, 64)
file1.Seek(count, io.SeekStart)
file2.Seek(count, io.SeekStart)
bufData := make([]byte, 1024, 1024)
total := int(count)
for {
readnum, err := file1.Read(bufData)
if err == io.EOF {
fmt.Println("文件读取完毕了")
file3.Close()
os.Remove(tempFile)
break
}
writeNum, err := file2.Write(bufData[:readnum])
total = total + writeNum
//将传输的进度存储到临时文件中
file3.Seek(0, io.SeekStart)
file3.WriteString(strconv.Itoa(total))
}
}
包:bufio
Go语言在io操作中,还提供了一个bufio的包,使用这个包可以大幅提高文件读写的效率
bufio包原理
bufio是通过缓冲来提高效率
io操作本身的效率并不低,低的是频繁的访问本地磁盘的文件,所以bufio就提供了缓冲区(分配一块内存)读和写都先在缓冲区,最后再读写文件,来降低访问本地磁盘的次数,从而提高效率。
简单的说就是:把文件读取进缓冲(内存)之后再读取的时候就可以避免文件系统的io从而提高速度。同理,在进行写操作时,先把文件写入缓冲(内存),然后由缓冲写入文件系统。
bufio
buffer:缓存
I/O:input/output
就是将io包下的Reader和Write进行了包装,带缓存的包装,提高读写的效率
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
//传输源文件
srcFile, _ := os.OpenFile("/Users/zuoliang/Documents/go/src/main/10_I_O/a.txt", os.O_RDWR, 0777)
defer srcFile.Close()
//bufio 包装
reader := bufio.NewReader(srcFile)
buf := make([]byte, 1024)
n, _ := reader.Read(buf)
fmt.Println(n)
fmt.Println(string(buf[:n]))
//读取键盘输入
//inputReader := bufio.NewReader(os.Stdin)
//str, _ := inputReader.ReadString('\n')
//fmt.Println("读取到键盘的输入为:", str)
//bufio 包装
writer := bufio.NewWriter(srcFile)
writernum, _ := writer.WriteString("hello")
fmt.Println("writernum", writernum)
//发现并没有写出到文件,是留在了缓冲区,所以我们需要调用flush刷新缓冲区
//将缓冲区的数据,写入文件
writer.Flush()
}
遍历文件夹
package main
import (
"fmt"
"os"
)
func main() {
listDir("/Users/zuoliang/Documents/go/src/main/10_I_O", 0)
}
func listDir(filePath string, tabint int) {
tab := "|--"
for i := 0; i < tabint; i++ {
tab = "| " + tab
}
dir := filePath
fileinfos, err := os.ReadDir(dir)
if err != nil {
fmt.Println("error", err)
return
}
for _, file := range fileinfos {
filename := dir + "/" + file.Name()
fmt.Printf("%s%s\n", tab, filename)
if file.IsDir() {
listDir(filename, tabint+1)
}
}
}