【Golang】关于Go语言中的IO操作

CSDN 2024-10-11 09:35:02 阅读 89

在这里插入图片描述

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑

🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。

🏆《博客》:Python全栈,Golang开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏:Go语言开发零基础到高阶实战

景天的主页:景天科技苑

在这里插入图片描述

文章目录

Go语言中的IO操作一、os包简介二、常用函数与示例1. 文件操作1.1 获取文件信息1.2 创建文件1.3 删除文件1.4 重命名或移动文件1.5 打开文件1.6 读取文件1.7 IO写

2. 目录操作2.1 创建目录2.2 更改当前工作目录

3. 环境变量3.1 获取环境变量

4. 程序退出5. 获取系统信息6. 执行外部命令1. 执行简单命令2. 获取命令输出3. 处理标准输出和标准错误

三、文件复制1. 自定义的文件复制实现2. 使用系统自带的io.Copy()方法复制3. 使用os.ReadFile和os.WriteFile来实现读取写入,从而实现复制4. io.CopyBuffer() 适合大文件复制

Go语言中的IO操作

在现代软件开发中,高效的输入输出(I/O)操作是提高程序性能的关键之一。Go语言提供了丰富的I/O操作接口,使得文件读写、网络通信等任务变得简单而高效。

Go语言的I/O操作主要通过标准库中的io包和os包实现。o包提供了一系列用于输入和输出操作的基本接口和原语。这些接口和原语构成了Go语言中处理I/O操作的基础,支持多种I/O设备的读写操作,包括文件、网络连接、内存缓冲区等。而os包则提供了对操作系统功能的封装,包括文件操作、进程管理等。

一、os包简介

os包是Go语言标准库中的一个重要包,提供了操作系统相关的功能接口。通过os包,我们可以执行各种底层操作,如读取和写入文件、获取系统信息、执行外部命令等。

二、常用函数与示例

1. 文件操作

1.1 获取文件信息

Stat:获取文件信息对象,符号链接会跳转。

Lstat:获取文件信息对象,符号链接不跳转。

<code>package main

import (

"fmt"

"os"

)

//看下fileinfo接口

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

// 获取某个文件的状态

//func Stat(name string) (FileInfo, error)

fileinfo, err := os.Stat("F:\\goworks\\src\\jingtian\\yufa\\type别名\\type别名.go")

if err != nil {

fmt.Println(err)

return

}

fmt.Println(fileinfo.Name()) // 文件名 demo01.go

fmt.Println(fileinfo.IsDir()) // 是否目录 false

fmt.Println(fileinfo.ModTime()) // 文件的修改时间 2024-09-26 10:03:01.0946679 +0800 CST

fmt.Println(fileinfo.Size()) // 文件大小 1542 字节数

fmt.Println(fileinfo.Mode()) // 权限 -rw-rw-rw-

}

在这里插入图片描述

1.2 创建文件

Create:创建文件,如果文件存在,则清空原文件。

<code>package main

import (

"fmt"

"os"

)

func main() { -- -->

//创建文件,文件不存在就创建文件,如果文件存在,则清空原文件。

// func Create(name string) (*File, error)

file, err := os.Create("example.txt")

if err != nil {

fmt.Println(err)

return

}

//关闭文件,延迟执行

defer file.Close()

//向文件中写内容

// func (f *File) WriteString(s string) (n int, err error)

_, err = file.WriteString("Hello, World!")

if err != nil {

fmt.Println(err)

}

fmt.Println("File created successfully")

}

文件创建,并且写入成功

在这里插入图片描述

创建的文件默认权限是0666

在这里插入图片描述

1.3 删除文件

Remove:删除文件或目录(单一文件)只能删除空文件夹,文件不存在会报错。如果文件夹中有文件也删不掉

RemoveAll:递归删除文件或目录,文件或目录不存在不报错,返回nil。

在这里插入图片描述

<code>package main

import (

"fmt"

"os"

)

func main() { -- -->

// 删除文件

// func Remove(name string) error

//文件不存在会报错

err := os.Remove("example.txt")

if err != nil {

fmt.Println(err)

} else {

fmt.Println("File removed successfully")

}

// 递归删除目录

// func RemoveAll(path string) error

//目录不存在返回nil 不会报错

//可以删除指定目录下的目录或文件

err = os.RemoveAll("testdir\\mydir.go")

if err != nil {

fmt.Println(err)

} else {

fmt.Println("Directory removed successfully")

}

}

在这里插入图片描述

1.4 重命名或移动文件

Rename:重命名文件或移动文件。

<code>package main

import (

"fmt"

"os"

)

func main() { -- -->

// 重命名文件

// func Rename(oldpath, newpath string) error

err := os.Rename("张三.txt", "新张三.txt")

if err != nil {

fmt.Println(err)

} else {

fmt.Println("File renamed successfully")

}

// 移动文件

err = os.Rename("新张三.txt", "testdir\\新张三.txt")

if err != nil {

fmt.Println(err)

} else {

fmt.Println("File moved successfully")

}

}

1.5 打开文件

使用os.Open函数可以打开一个文件,返回一个*os.File类型的文件对象。如果文件打开失败,会返回一个错误。

package main

import (

"fmt"

"os"

)

func main() {

//func Open(name string) (*File, error)

file, err := os.Open("example.txt")

if err != nil {

fmt.Println("Error opening file:", err)

return

}

defer file.Close()

fmt.Println("File opened successfully")

//打印打开的文件,是个内存地址

fmt.Println("Reading file:", file)

}

在这里插入图片描述

打开文件常用方式二:os.OpenFile

在Go语言中,文件的读写操作是使用非常频繁的功能之一。os.OpenFile函数是Go标准库中用于打开文件的强大工具,它允许以多种模式打开文件,并可以设置文件的权限。

os.OpenFile函数简介

os.OpenFile函数用法如下:

func OpenFile(name string, flag int, perm FileMode) (File, error)

name:要打开的文件名或路径。

flag:打开文件的模式,可以是多个标志的按位或组合。常见的标志包括:

os.O_RDONLY:只读模式。

os.O_WRONLY:只写模式。

os.O_RDWR:读写模式。

os.O_APPEND:追加模式,在文件末尾写入数据而不覆盖原有数据。只要有这个参数,就会采用追加模式

os.O_CREATE:如果文件不存在则创建文件。

os.O_EXCL:与O_CREATE一起使用,可执行权限。

os.O_SYNC:同步模式,将文件更改刷新到磁盘。

os.O_TRUNC:截断模式,清空文件内容。

perm:文件权限,表示文件的读写权限,默认为0666(即所有用户都可读写)。

返回值是一个指向打开文件的File对象和一个可能出现的错误。如果文件打开成功,则可以通过该文件对象进行读取和写入操作。

使用os.OpenFile写文件

以下是一个使用os.OpenFile函数写文件的示例:

<code>package main

import (

"fmt"

"os"

)

func main() { -- -->

// 以追加、创建、读写模式打开文件

file, err := os.OpenFile("./example.txt", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)

if err != nil {

fmt.Println("打开文件失败:", err)

return

}

defer file.Close() // 确保在函数执行完毕后关闭文件

// 写入文件

_, err = file.Write([]byte("Hello, Go!\n"))

if err != nil {

fmt.Println("写入文件失败:", err)

return

}

fmt.Println("文件写入成功")

}

将内容追加到文件中

在这里插入图片描述

1.6 读取文件

使用os.File对象的Read方法可以从文件中读取数据。Read方法会将数据读取到一个字节切片中,并返回读取的字节数和可能发生的错误。

需要借助 file.Read([]byte)读取 ,将file中的数据读取到 []byte 中, n,err n读取到的行数,err 错误,EOF错误,就代表文件读取完毕了

一直调用read,就代表光标往后移动…

以下是一个使用os.Open函数读文件的示例:

<code>package main

import (

"fmt"

"os"

)

// 读取文件数据

func main() { -- -->

// 我们习惯于在建立连接时候通过defer来关闭连接,保证程序不会出任何问题,或者忘记关闭

// 建立连接

file, _ := os.Open("example.txt")

// 关闭连接

defer file.Close()

// 读代码 ,Go 的错误机制,让我们专心可以写业务代码。

// 1、创建一个容器 (二进制文本文件--0100101010 => 读取流到一个容器 => 读取容器的数据)

//一次性读取的内容长度为切片的长度,切片长度为2,容量为1024

bs := make([]byte, 2, 1024) // 缓冲区,可以接受我们读取的数据 这里设置的是一次性读取两个字节

fmt.Printf("字节切片数据类型%T\n", bs)

// 2、读取到缓冲区中。 // 汉字一个汉字占 3个字节

// func (f *File) Read(b []byte) (n int, err error) 返回读到的字节数和错误

// At end of file, Read returns 0, io.EOF. 读到文件末尾返回0和io.EOF

n, err := file.Read(bs)

fmt.Println(n)

fmt.Println(err)

//每次读取光标移动切片的长度,将读取的内容存到切片中。如果读取的文件是文本数据,可以通过string方法将切片中的内容转换为字符串

//如果是MP4呀,音乐,图片等文件,就不要用string转换了

fmt.Println(string(bs)) // 读取到的字符串 ab

//继续往下读

// 光标不停的向下去指向,读取出来的内容就存到我们的容器中。

file.Read(bs)

fmt.Println(string(bs)) // 读取到的字符串 cd

file.Read(bs)

fmt.Println(string(bs)) // 读取到的字符串 e

//读到文件末尾,得到的n是0,err是EOF

n, err = file.Read(bs)

fmt.Println(n)

fmt.Println(err) // EOF ,读取到了文件末尾。err就会返回EOF。

//最后读到了e,将原来切片中的cd的c覆盖了,得到的最终结果是ed

fmt.Println(string(bs)) // 读取到的字符串

}

在这里插入图片描述

1.7 IO写

写的话,要考虑文件的权限问题,大致流程如下

建立连接 (设置权限:可读可写,扩充这个文件的append) os.OpenFile

关闭连接

写入文件常用的方法

file.Write

file.WriteString

先看下example.txt文件

在这里插入图片描述

<code>package main

import (

"fmt"

"os"

)

func main() { -- -->

fileName := "example.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 ASCII编码

// func (f *File) Write(b []byte) (n int, err error)

//write方法 写入的是二进制数据字节流,即字节切片

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)

}

在这里插入图片描述

再看下example.txt文件,可以看到我们写入的内容全部追加到文件末尾

在这里插入图片描述

如果打开文件的方式不包含os.O_APPEND 则是将源文件覆盖写。os.O_APPEND的意思是,在打开文件时,将光标移动到文件末尾

2. 目录操作

2.1 创建目录

Mkdir:使用指定的权限和名称创建一个目录,目录存在会报错。

MkdirAll:递归创建目录,包括任何必要的上级目录,目录存在不报错。

<code>package main

import (

"fmt"

"os"

)

func main() { -- -->

// 创建单个目录

// func Mkdir(name string, perm FileMode) error

// os.ModePerm 是0777的权限

//mkidr创建目录,目录存在的情况下,会报错 Cannot create a file when that file already exists.

err := os.Mkdir("singledir", os.ModePerm)

if err != nil {

fmt.Println(err)

} else {

fmt.Println("Single directory created successfully")

}

// 递归创建目录,目录存在不报错

// func MkdirAll(path string, perm FileMode) error

err = os.MkdirAll("nested/dirs/test", os.ModePerm)

if err != nil {

fmt.Println(err)

} else {

fmt.Println("Nested directories created successfully")

}

}

2.2 更改当前工作目录

Getwd:获取当前工作目录。

Chdir:修改当前工作目录。

package main

import (

"fmt"

"os"

)

func main() {

// 获取当前工作目录

path, err := os.Getwd()

if err != nil {

fmt.Println(err)

return

}

fmt.Println("Current working directory:", path)

// 修改当前工作目录

err = os.Chdir("../")

if err != nil {

fmt.Println(err)

return

}

path, err = os.Getwd()

if err != nil {

fmt.Println(err)

return

}

fmt.Println("New working directory:", path)

}

在这里插入图片描述

3. 环境变量

3.1 获取环境变量

Environ:返回所有的环境变量,格式为“key=value”的字符串切片。

Getenv:检索并返回名为key的环境变量的值,如果不存在则返回空字符串。

Setenv:

函数定义:func Setenv(key, value string) error

功能:设置名为key的环境变量,如果出错会返回该错误。

<code>package main

import (

"fmt"

"os"

"strings"

)

func main() { -- -->

// 设置环境变量

err := os.Setenv("MY_ENV_VAR", "my_value")

if err != nil {

fmt.Println(err)

} else {

fmt.Println("Environment variable set successfully")

}

// 获取并打印刚设置的环境变量

value := os.Getenv("MY_ENV_VAR")

fmt.Println("MY_ENV_VAR:", value)

//查看所有环境变量

envs := os.Environ()

for _, env := range envs {

cache := strings.Split(env, "=")

fmt.Printf("key=%v value=%v\n", cache[0], cache[1])

}

// 清除所有环境变量(谨慎使用)

// err = os.Clearenv()

// if err != nil {

// fmt.Println(err)

// } else {

// fmt.Println("All environment variables cleared")

// }

}

在这里插入图片描述

4. 程序退出

Exit:让当前程序以给出的状态码code退出。一般来说,状态码0表示成功,非0表示出错。程序会立刻终止,并且defer的函数不会被执行。

package main

import (

"fmt"

"os"

)

func main() { -- -->

// 正常退出

fmt.Println("Exiting the program successfully")

//os.Exit(0)

// 异常退出(不会执行到这里的代码)

fmt.Println("This line will not be executed")

os.Exit(1)

}

在这里插入图片描述

5. 获取系统信息

Hostname:返回内核提供的主机名。

Getuid:返回调用者的用户ID。

Getgid:返回调用者的组ID。

Getpid:返回调用者所在进程的进程ID。

<code>package main

import (

"fmt"

"os"

)

func main() { -- -->

// 获取主机名

hostname, err := os.Hostname()

if err != nil {

fmt.Println(err)

return

}

fmt.Println("Hostname:", hostname)

// 获取用户ID

uid := os.Getuid()

fmt.Println("User ID:", uid)

// 获取组ID

gid := os.Getgid()

fmt.Println("Group ID:", gid)

// 获取进程ID

pid := os.Getpid()

fmt.Println("Process ID:", pid)

}

在这里插入图片描述

6. 执行外部命令

os/exec包用于执行外部命令,它提供了更高级别的接口来创建和管理进程。

1. 执行简单命令

<code>package main

import (

"fmt"

"os/exec"

)

func main() { -- -->

//查看系统中是否安装有该命令

cmd_path, err := exec.LookPath("ipconfig")

if err != nil {

fmt.Println(err)

return

}

fmt.Println("系统中有该命令,命令路径为:", cmd_path)

//Command每个参数都不能包含空格。多个参数,用引号包裹,逗号隔开

cmd := exec.Command("ipconfig")

// 如果不需要命令的输出,直接调用cmd.Run()即可

err2 := cmd.Run()

if err2 != nil {

fmt.Println("Error:", err2)

} else {

fmt.Println("Command executed successfully")

}

}

在这里插入图片描述

使用exec.Command函数创建一个代表外部命令的Cmd对象,然后使用Run方法执行命令并等待其完成。

我们使用 exec.Command() 创建了一个执行 ipconfig 命令的 *exec.Cmd,然后使用 Run() 方法启动了命令。

请注意,使用 exec.Command() 创建的命令默认不会有任何输出。如果我们想获取命令的标准输出,我们可以使用 Output() 或 CombinedOutput() 方法。如果我们想获取命令的标准错误输出,我们需要单独设置 Cmd.Stderr 字段。

2. 获取命令输出

使用Output方法运行命令并返回其标准输出的内容。

<code>package main

import (

"fmt"

"os/exec"

)

func main() { -- -->

//查看系统中是否安装有该命令

cmd_path, err := exec.LookPath("ipconfig")

if err != nil {

fmt.Println(err)

return

}

fmt.Println("系统中有该命令,命令路径为:", cmd_path)

cmd := exec.Command("ipconfig")

output, err := cmd.Output()

if err != nil {

fmt.Println("Error:", err)

} else {

fmt.Println("Command output:", string(output))

}

}

3. 处理标准输出和标准错误

CombinedOutput

func (c *Cmd) CombinedOutput() ([]byte, error)

说明:运行命令并返回组合到一起的标准输出和标准错误

package main

import (

"fmt"

"os/exec"

)

func main() {

//查看系统中是否安装有该命令

cmd_path, err := exec.LookPath("ping")

if err != nil {

fmt.Println(err)

return

}

fmt.Println("系统中有该命令,命令路径为:", cmd_path)

// 创建一个新的命令,例如运行dir命令

cmd := exec.Command("ping", "www.baidu.com")

//获取标准输出

out, err := cmd.CombinedOutput()

if err != nil {

fmt.Println(err)

return

}

fmt.Println("标准输出为:", string(out))

}

在这里插入图片描述

三、文件复制

文件复制需要考虑的问题:

1、拿到需求,分析需求,编辑函数 copy()

2、编写参数(源文件,目标地址,缓冲区大小)

3、读取源文件,保存到缓冲区中

4、读取缓冲区的内容,将内容写入到新文件中

5、确保源文件读取完毕了,使用到for()循环。循环结束标志 io.EOF或者读取到的字节数为0

1. 自定义的文件复制实现

我们创建个目录utils。里面创建个copy.go

<code>package utils

import (

"fmt"

"io"

"os"

)

// Copy 方法需要参数为:source 源文件 ,destination 目标文件,缓冲区大小

func Copy(source, destination string, bufferSize int) { -- -->

// 读取文件,读文件open方法就可以

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)

}

}

}

在这里插入图片描述

调用我们的copy方法,实现文件copy

<code>package main

import "jingtian/yufa/io操作/utils"

func main() { -- -->

source := "C:\\Users\\dell\\Desktop\\appsearch.zip"

dest := "F:\\goworks\\src\\jingtian\\yufa\\io操作\\目标文件.zip"

utils.Copy(source, dest, 1024)

}

在这里插入图片描述

运行,可以将源文件复制到目标地址

在这里插入图片描述

2. 使用系统自带的io.Copy()方法复制

<code>// Copy2 使用系统自带的io.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()

// 具体的实现

// 不用我们自己写循环读取写入了,直接调用系统的io.Copy()方法

// func Copy(dst Writer, src Reader) (written int64, err error) 返回写入的字节数和错误

written, err := io.Copy(destinationFile, sourceFile)

if err != nil {

fmt.Println("复制时出现错误", err)

return

}

fmt.Println("文件的字节大小:", written)

}

3. 使用os.ReadFile和os.WriteFile来实现读取写入,从而实现复制

// Copy3 使用os.ReadFile和os.WriteFile来实现读取写入,从而实现复制

func Copy3(source, destination string) {

// func ReadFile(name string) ([]byte, error)

fileBuffer, _ := os.ReadFile(source)

//func WriteFile(name string, data []byte, perm FileMode) error

err := os.WriteFile(destination, fileBuffer, 0644)

if err != nil {

fmt.Println(err)

return

}

fmt.Println("文件复制完成")

}

4. io.CopyBuffer() 适合大文件复制

// Copy4 io.CopyBuffer() 适合大文件

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

// 不用我们自己写循环读取写入了,直接调用系统的io.Copy()方法

//io.copy这个适合小文件,大文件使用io.CopyBuffer

// func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error)

written, err := io.CopyBuffer(destinationFile, sourceFile, buf)

if err != nil {

fmt.Println("复制时出现错误", err)

return

}

fmt.Println("文件的字节大小:", written)

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。