day12 IO包

【os】

os 基础使用

  • os.Stat
package main

import (
	"fmt"
	"os"
)

func main() {
	/* os 模块  */

	// os.Stat 获取文件信息 :FileInfo
	fileInfo, err := os.Stat("/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/Readme.md")
	if err != nil {
		return
	}
	fmt.Println("该文件文件名:", fileInfo.Name())
	fmt.Println("该文件大小:", fileInfo.Size(), "【单位:字节】")
	fmt.Println("该文件是否是文件夹:", fileInfo.IsDir())
	fmt.Println("该文件权限:", fileInfo.Mode())
	fmt.Println("该文件更新时间:", fileInfo.ModTime())

}

os 创建文件夹

  • os.Mkdir
  • os.MkdirAll
  • os.Remove
  • os.RemoveAll
package main

import (
	"fmt"
	"os"
)

func main() {
	/* os 模块  */

	// os 创建目录,创建文件
	/*
		- 文件路径
			- 相对路径 reference path,相对某个文件或者某个目录

			- 绝对路径  absolute path, 从盘符开始
	*/
	// os.Mkdir(文件路径,权限0777) 创建一个文件夹。1.存在,2.不存在,创建
	err1 := os.Mkdir("/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/noExistFile", os.ModePerm)
	if err1 != nil {
		fmt.Println(err1) // mkdir /Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/noExistFile: file exists
	} else {
		fmt.Println("noExistFile创建成功")
	}

	// os.MkdirAll 递归创建文件夹
	err2 := os.MkdirAll("/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/noExistFile/aa/bb/cc", os.ModePerm)
	if err2 != nil {
		fmt.Println(err2)
	} else {
		fmt.Println("多层文件夹创建成功")
	}

	// os.remove 移除文件夹【只能删除空目录】,删除文件
	err3 := os.Remove("/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/noExistFile/aa/bb/cc")
	if err3 != nil {
		fmt.Println(err3) // : directory not empty  文件夹内还有文件,无法删除
	} else {
		fmt.Println("文件夹已删除")
	}

	// os.RemoveAll 全部删除,该文件夹内的文件也被删除
	err4 := os.RemoveAll("/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/noExistFile/aa/bb/cc")
	if err4 != nil {
		fmt.Println(err4) // 强制删除文件夹
	} else {
		fmt.Println("有内容的文件夹,removeAll 全部删除")
	}

}

os 创建文件

  • os IO写文件。 注意权限问题:可读,可写,可追加, os.O_RDONLY|os.O_WRONLY|os.O_APPEND
  • os.OpenFile 打开文件 并指定权限[只读,只写,读写,追加写等]
  • os.OpenFile 如果没有 赋予 追加的权限,文件将会被清空后重新写一个新文件。
  • 模式:os.O_RDONLY|os.O_WRONLY|os.O_APPEND|os.O_CREATE
  • 权限:os.ModePerm
package main

import (
	"fmt"
	"os"
)

func main() {
	/* os 模块  */
	// os IO写文件。 注意权限问题:可读,可写,可追加, os.O_RDONLY|os.O_WRONLY|os.O_APPEND
	// os.OpenFile 打开文件 并指定权限[只读,只写,读写,追加写等]
	// os.OpenFile 如果没有 赋予 追加的权限,文件将会被清空后重新写一个新文件。
	
	file3, _ := os.OpenFile("/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/noExistFile/WriteFile.txt", os.O_RDONLY|os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.ModePerm)
	fmt.Println("file3:", file3)
	defer file3.Close()

	bs2 := []byte{65, 66, 67, 68}
	file3.Write(bs2)             // Write 只能写入字节码
	file3.WriteString("追加一段字符传") // 写入一段字符串

}

os 实现 Copy 文件的三种方式

package main

import (
	"fmt"
	"io"
	"os"
)

func copyFile01(src string, dest string, bufferSize int) error {
	/*
		src : 原始文件
		dest : 拷贝文件
		bufferSize: 缓冲区大小
	*/
	// 1. 读取原文件

	srcFile, readFileErr := os.Open(src)

	// 2. 处理读取文件错误
	if readFileErr != nil {
		fmt.Println("读取原文件错误:", readFileErr)
	}
	// 3. 打开目标文件. 赋予:读写和创建模式. 赋予:666权限:【4读2写1执行】(用户:可读写,用户组:可读写,其他:可读写)
	destFile, writeFileErr := os.OpenFile(dest, os.O_RDWR|os.O_CREATE, 0666)
	if writeFileErr != nil {
		fmt.Println("写入新文件错误:", writeFileErr)
	}

	// 4. copy 业务逻辑具体实现
	// - 4.1 字节缓冲区 : 设定读取字节切片。容量由外部传入
	//字节缓冲区
	byteSlice := make([]byte, bufferSize)
	fmt.Printf("定义:byteSlice内存地址 :%p\n", byteSlice) // :0xc0000cec00

	// - 4.2 使用for循环读取
	for {
		// - 4.3 字节类型切片接收数据,并写入数据
		// 读取数据到字节切片内
		readByteCount, readErr := srcFile.Read(byteSlice)
		fmt.Printf("读前:byteSlice内存地址 :%p\n", byteSlice) // :0xc0000cec00
		//fmt.Printf("读前:byteSlice内容 :%v\n", byteSlice)   // :0xc0000cec00
		fmt.Println("readByteCount:", readByteCount, "readErr:", readErr)
		//  readByteCount读取字节长度为:0 或者readErr为:io.EOF 表示文件读取完毕
		if readByteCount == 0 || readErr == io.EOF {
			fmt.Println("原文件读取完毕")
			break // 终止循环
		} else if readErr != nil {
			fmt.Println("读取文件出现异常!", readErr)
			// 终止函数继续执行,返回错误
			return readErr
		}
		// 将 字节切片写入到 新文件中
		fmt.Printf("写前:byteSlice内存地址 :%p\n", byteSlice) // :0xc0000cec00
		//fmt.Printf("写前:byteSlice内容 :%v\n", byteSlice)   // :0xc0000cec00

		writeByteCount, writeErr := destFile.Write(byteSlice[:readByteCount])
		fmt.Println("writeByteCount:", writeByteCount, "writeErr:", writeErr)
		if writeErr != nil {
			fmt.Println("写入文件失败", writeErr)
			return writeErr
		}

	}
	// 循环会影响 defer 执行
	// 2. 延时关闭 读IO 连接
	defer srcFile.Close() // 关闭 读io连接
	// 5. 关闭写入IO连接
	destFile.Close()
	return nil
}
func copyFile02(src string, dest string) {
	srcFile, readFileErr := os.Open(src)
	if readFileErr != nil {
	}
	defer srcFile.Close()
	destFile, writeFileErr := os.Create(dest)
	if writeFileErr != nil {
	}
	defer destFile.Close()
	// 使用 io 下的 Copy 拷贝文件
	io.Copy(destFile, srcFile)
}
func copyFile03(src string, dest string) {
	fileBuffer, _ := os.ReadFile(src)
	os.WriteFile(dest, fileBuffer, 0666)
}
func main() {
	/* os 模块  */

	// os 文件复制 .
	/*
		流程: 原文件 , 目标文件, 缓冲区
			1. 读取原文件,将读取的数据存储到缓冲区
			2. 读取缓冲区的数据,将内容写入到新的文件
			3. 确保读取原文时,是否读取完毕。 通过 EOF 判断
	*/
	src := "/Users/dengshuai/my_files/个人文件/家人/IMG20181201091508.jpg"
	dest1 := "/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/noExistFile/aa/bb/family1.png"
	dest2 := "/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/noExistFile/aa/bb/family2.jpg"
	dest3 := "/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/noExistFile/aa/bb/family3.jpg"
	copyErr := copyFile01(src, dest1, 1024)
	fmt.Println("copyErr:", copyErr)
	copyFile02(src, dest2)
	copyFile03(src, dest3)

}

Seeker使用

  • 设置光标位置,读取文件
  • offset 偏移量
  • whence 当前光标的位置
  • Seek(offset int64, whence int) (int64, error)
package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
	/*
		Seeker接口使用
			- 设置光标位置,读取文件

		type Seeker interface {
			// offset 偏移量
			// whence 当前光标的位置
			Seek(offset int64, whence int) (int64, error)

			whence 如何设置参数:
			// Seek whence values.
			const (
				SeekStart   = 0 // seek relative to the origin of the file。文件开头
				SeekCurrent = 1 // seek relative to the current offset  相对于当前光标的位置
				SeekEnd     = 2 // seek relative to the end 文件的末尾
			)
		}
	*/

	// 1. 读取文件
	fileObj, _ := os.OpenFile("/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/noExistFile/WriteFile.txt", os.O_RDWR, os.ModePerm)

	// 2. close 文件
	defer fileObj.Close()

	// 3. 测试 Seeker 方法
	fileObj.Seek(2, io.SeekStart) // 从文件开头,往后偏移两个光标位置,默认光标从0开始
	//fileObj.Seek(-2, io.SeekEnd) // 从文件结尾,往前偏移两个光标位置
	buffer := make([]byte, 1) // 读取一个字节
	n, _ := fileObj.Read(buffer)
	fmt.Println(n)
	fmt.Println(string(buffer))

	fileObj.Seek(-1, io.SeekCurrent) // 从当前光标位置,再往前移动1位
	// 从当前位置追加 字符串
	fileObj.WriteString("haha")

}

文件断点续传

package main

import (
	"fmt"
	"io"
	"os"
	"strconv"
)

func main() {

	/* 通过 io.Seeker 实现io断点续传 */

	// 传输原文件地址
	srcFile := "/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/client/a.png"

	// 传输目标地址
	destFile := "/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/server/new2.png"

	// 临时文件记录
	tmpFile := "/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/temp.txt"

	// 逻辑思路
	//fileSrc1, _ := os.Open(srcFile)                                           // 读取原文件
	fileSrc1, _ := os.OpenFile(srcFile, os.O_CREATE|os.O_RDWR, os.ModePerm)   // 读取原文件
	fileDest1, _ := os.OpenFile(destFile, os.O_CREATE|os.O_RDWR, os.ModePerm) // 写入新文件
	fileTmp1, _ := os.OpenFile(tmpFile, os.O_CREATE|os.O_RDWR, os.ModePerm)   // 临时文件存储seek记录
	defer fileSrc1.Close()
	defer fileDest1.Close()

	// 1. 读取 temp.txt
	fileTmp1.Seek(0, io.SeekStart)
	buf := make([]byte, 1024, 1024) // 设置自己切片接收读取的字节
	n, _ := fileTmp1.Read(buf)      // 读取buf

	// 2. 转换string 为 数字
	countStr := string(buf[:n])
	count, _ := strconv.ParseInt(countStr, 10, 64) // 将读取的字符转换成数量
	fmt.Println("读取临时文件tmp.txt的数量:", count)

	// 3. 设置新的偏移量
	fileSrc1.Seek(count, io.SeekStart)
	fileDest1.Seek(count, io.SeekStart)

	// 4. 边读边写
	bufData := make([]byte, 1024, 1025)
	// 5. 记录读取多少字节
	total := int(count)

	for {
		readNum, err := fileSrc1.Read(bufData)
		println("readNum:", readNum)
		if err == io.EOF {
			// 读取完毕
			fmt.Println("文件传输完毕!")
			fileTmp1.Close()
			os.Remove(tmpFile) // 移除临时文件
			break
		}

		// 向目标文件写入字符
		writeNum, err := fileDest1.Write(bufData[:readNum])
		total += writeNum

		// tmp.txt 存放临时写入多少字符量
		fileTmp1.Seek(0, io.SeekStart)
		fileTmp1.WriteString(strconv.Itoa(total))
		fmt.Println(total)

		// 模拟异常中断,导致数据传输失败
		if total > 1022224 {
			panic("断电了")
		}
	}

}

遍历文件夹

  • 递归 方式实现遍历文件目录
package main

import (
	"fmt"
	"os"
)

func tree(dir string, level int) {
	// mac 版本哈!!!
	// 编写层级
	tabString := "|--"
	for i := 0; i < level; i++ {
		tabString = "|--" + tabString
	}
	// os.ReadDir 获取目录信息。  类型:[]DirEntry
	fileInfos, err := os.ReadDir(dir)
	if err != nil {
		//log.Println(err)
	}
	
	for _, eachFile := range fileInfos {
		filePath := dir + string(os.PathSeparator) + eachFile.Name()
		filePathFmt := tabString + dir + string(os.PathSeparator) + eachFile.Name()
		fmt.Println(filePathFmt)
		if eachFile.IsDir() {
			tree(filePath, level+1)
		}
	}

}

func main() {
	/*
		遍历文件夹
			- 递归 方式实现遍历文件目录

	*/

	dir := "/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO"
	tree(dir, 0)

}

Go自带的IO操作包 bufio

  • 使用bufio 可以大幅度提升文件读写效率
  • buf 缓冲区
  • 默认io操作效率还不错,还会频繁访问本地磁盘效率低。使用bufio修改缓冲区的大小
  • 读写到新的缓冲区内。一次性写入或读取,减少磁盘访问频率。
package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
)

func main() {
	/*
		go 语言自带的IO操作包
			- 使用bufio 可以大幅度提升文件读写效率
			- buf 缓冲区
			- 默认io操作效率还不错,还会频繁访问本地磁盘效率低。使用bufio修改缓冲区的大小
			- 读写到新的缓冲区内。一次性写入或读取,减少磁盘访问频率。
	*/

	// bufio 的应用

	file, err := os.Open("/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/demo05Seeker接口使用.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)
	//fmt.Println("内容:", string(buf[:n]))

	// bufio 自己的方法
	s1, _, _ := bufioReader.ReadLine() // 读一行
	fmt.Println(string(s1))

	// bufio 读取键盘输入流
	inputerReader := bufio.NewReader(os.Stdin)
	// delim 分隔符参数,必须是字符 byte
	readString, _ := inputerReader.ReadString('\n')
	fmt.Println("读取键盘输入信息:", readString)

}

bufio包的使用:写文件

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	/* 使用 bufio 写入文件*/
	writeFilePath := "/Users/dengshuai/my_files/CProject/GoRoad/src/GoStudy/golang_base_17/day12IO/noExistFile/writeBufio.txt"
	writeFile, _ := os.OpenFile(writeFilePath, os.O_CREATE|os.O_RDWR, os.ModePerm)
	defer writeFile.Close()

	// 使用bufio 写入
	bufWriter := bufio.NewWriter(writeFile)
	writeNum, _ := bufWriter.WriteString("12345")
	fmt.Println("writeNum:", writeNum)

	// 手动刷新进新文件
	// 如果不实用flush,内容会存到缓冲区中。不会写入到文件
	bufWriter.Flush()

}

posted @ 2024-07-02 08:18  染指未来  阅读(8)  评论(0编辑  收藏  举报