文章

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())

}

image-20221226231434771

科普 权限

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("文件夹包括子文件删除完毕啦~")

}

image-20221226234301268

创建文件

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)

}

image-20221226235034640

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)
}

image-20221227130528530

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)
}

image-20221227131422067

文件复制

源文件-读取-程序-写出-目标文件

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("我是追加数据")
}

断点续传

首先思考几个问题:

  1. 如果你要传的文件,比较大,那么是否有方法可以缩短耗时?
  2. 如果在文件传递过程中,程序因各种原因被迫中断了,那么下次再重启时,文件是否还需要重头开始?
  3. 传递文件的时候,支持暂停和恢复么?即使这两个操作分布在程序被杀前后。

这些都是可以通过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))
   }
}

image-20221229125606330

包: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()
}

image-20221229131837782

遍历文件夹

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)
      }
   }

}
License:  CC BY 4.0 test