一、对文件的读写
在 Go 语言中,
io
包提供了基本的接口,用于 I/O 原语。它的主要目的是将这些原语抽象化,使得它们在不同的实现中可以通用。
Reader
接口
以下是一个对文件中内容读取的一个示例:
package main
import (
"fmt"
"io"
"os"
)
func main() {
// 打开文件 text.txt,如果打开失败,打印错误信息并返回
file, err := os.Open("./text.txt")
if err != nil {
fmt.Println(err)
return
}
// 确保文件在函数结束时关闭
defer file.Close()
// 定义一个长度为 128 的字节数组作为缓冲区
var buf [128]byte
// 存储文件内容的字节切片
var content []byte
for {
// 从文件中读取数据到缓冲区,n 表示读取的字节数
n, err := file.Read(buf[:])
// 如果到达文件末尾,跳出循环
if err == io.EOF {
break
}
// 如果读取过程中出现错误,打印错误信息并返回
if err != nil {
fmt.Println(err)
return
}
// 将读取到的数据添加到 content 切片中
content = append(content, buf[:n]...)
}
// 将字节切片转换为字符串并打印
fmt.Println(string(content))
}
Writer接口
以下是写入文件的操作:
注:每次写入的时候原文件中的内容都会被覆盖。
// Writer接口的定义和实现
package main
import (
"fmt"
"os"
)
func main() {
// 打开文件 text.txt,如果文件不存在则创建,如果文件存在则清空内容
file, err := os.Create("./text2.txt")
if err != nil {
fmt.Println(err)
return
}
// 确保文件在函数结束时关闭
defer file.Close()
// 定义一个字符串
str := "Hello,World!"
// 将字符串转换为字节切片
data := []byte(str)
// 将字节切片写入文件
_, err = file.Write(data)
// 如果写入过程中出现错误,打印错误信息并返回
if err != nil {
fmt.Println(err)
return
}
}
写入一个 Hello,World!
写入一个”你好世界!“
可以看出原来文件中的文本被替换了。
copy接口
以下是复制文件的操作:
// io.Copy的使用
package main
import (
"fmt"
"io"
"os"
)
func main() {
// 打开文件 text.txt,如果文件不存在则创建,如果文件存在则清空内容
begin, err := os.Open("./text2.txt")
if err != nil {
fmt.Println(err)
return
}
// 确保文件在函数结束时关闭
defer begin.Close()
// 打开文件 text2.txt,如果文件不存在则创建,如果文件存在则清空内容
end, err := os.Create("./text3.txt")
if err != nil {
fmt.Println(err)
return
}
// 确保文件在函数结束时关闭
defer end.Close()
// 将文件 text.txt 的内容复制到文件 text2.txt 中
_, err = io.Copy(end, begin)
// 如果复制过程中出现错误,打印错误信息并返回
if err != nil {
fmt.Println(err)
return
}
}
begin中的内容会自动覆盖end中的内容。
bufio的使用
常见的方法如下:
// bufio的使用
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func wr() {
// 参数2 是文件的打开方式,os.O_CREATE|os.O_WRONLY|os.O_APPEND 表示创建文件并以写入模式打开,文件不存在则创建,文件存在则在文件末尾追加内容
// 参数3 是文件的权限,0666 表示文件所有者、组和其他用户都有读写权限
// w写 r读 a追加 x执行
file, err := os.OpenFile("./text.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
// 获取writer对象
writer := bufio.NewWriter(file)
writer.WriteString("hello bufio")
// 刷新缓冲区
writer.Flush()
}
func rd() {
file, err := os.Open("./text.txt")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
reader := bufio.NewReader(file)
for {
line, _, err := reader.ReadLine()
if err != io.EOF {
fmt.Println(err)
break
}
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(line))
}
}
func main() {
wr()
}
以这种方式进行读写不会覆盖原来的文件
读取文本文件的时候是按行读取的
ioutil库
// ioutil的使用
package main
import (
"fmt"
"io/ioutil"
)
func wr() {
err := ioutil.WriteFile("./text.txt", []byte("hello ioutil"), 0666)
if err != nil {
fmt.Println(err)
return
}
}
func re() {
content, err := ioutil.ReadFile("./text.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(content))
}
func main() {
wr()
re()
}
这个工具库相对于其他的工具库更为简单。相应的函数已经被很好的打包完成了。
二、cat命令
package main
import (
"bufio"
"flag"
"fmt"
"io"
"os"
)
// cat 函数接收一个 bufio.Reader 指针作为输入,用于读取文件或标准输入的内容
func cat(r *bufio.Reader) {
// 开始一个无限循环,用于逐行读取数据
for {
// 从 bufio.Reader 中读取一行数据,直到遇到换行符 '\n',存储在 buf 中,并将可能的错误存储在 err 中
buf, err := r.ReadBytes('\n')
// 如果读取到文件末尾,打印错误信息并退出循环
if err == io.EOF {
fmt.Println(err)
break
}
// 将读取到的一行数据输出到标准输出
fmt.Fprintf(os.Stdout, "%s", buf)
}
}
func main() {
// 解析命令行参数
flag.Parse()
if flag.NArg() == 0 {
// 从标准输入读取数据并输出到标准输出
cat(bufio.NewReader(os.Stdin))
}
// 依次处理每个文件
for i := 0; i < flag.NArg(); i++ {
// 打开文件
f, err := os.Open(flag.Arg(i))
if err!= nil {
fmt.Fprintf(os.Stderr, "cat: %s: %s\n", flag.Arg(i), err)
continue
}
// 读取文件内容并输出到标准输出
cat(bufio.NewReader(f))
// 关闭文件
f.Close()
}
}
上述代码中,
cat
函数的主要功能是从
bufio.Reader
中逐行读取数据并输出到标准输出。在
for
循环中,使用
r.ReadBytes('\n')
方法读取一行数据,当遇到文件结束符
io.EOF
时,打印错误信息并退出循环,否则将读取的数据输出到标准输出。在
main
函数中,首先解析命令行参数,若没有命令行参数,则从标准输入读取数据;若有命令行参数,则依次打开文件,调用
cat
函数读取文件内容并输出,最后关闭文件。
**注:在
cat
函数中,当遇到
io.EOF
时,打印错误信息可能不是最佳做法,因为
EOF
不是错误,而是文件结束的标志。可以考虑修改为不打印错误信息,仅退出循环。**
如果文件中有内容就会打印出文件中的内容。
如果直接执行go run mian.go会打印输出用户输入的内容。
三、包
在 Go 语言中,包(package)是组织代码的一种方式,它可以将相关的函数、变量和类型组合在一起,以便于代码的管理、复用和维护。以下是关于 Go 语言包的一些重要信息:
1. 包的声明
在 Go 文件的开头,使用
package
关键字来声明包的名称。
package main
package main
是一个特殊的包,它表示该文件是一个可执行程序的入口点。- 对于其他包,可以使用其他名称,如
package utils
或package math
。
2. 导入包
使用
import
语句来导入其他包。
import (
"fmt"
"os"
"github.com/yourusername/yourpackage"
)
- 导入标准库中的包,如
fmt
和os
。 - 导入第三方包,如
github.com/yourusername/yourpackage
。
3. 包的可见性
- 在 Go 中,标识符(函数、变量、类型)的名称首字母大小写决定了其可见性:
- 首字母大写的标识符是导出的,可以被其他包访问。
- 首字母小写的标识符是未导出的,只能在当前包内使用。
package mypackage
// 导出的函数
func ExportedFunction() {
}
// 未导出的函数
func unexportedFunction() {
}
在另一个包中,可以调用
mypackage.ExportedFunction()
,但不能调用
mypackage.unexportedFunction()
。
4. 包的初始化
包可以包含一个
init
函数,它会在包被导入时自动执行。
package mypackage
import "fmt"
func init() {
fmt.Println("Initializing mypackage")
}
- 一个包可以有多个
init
函数,它们会按照声明的顺序执行。
5. 标准库包
Go 标准库包含了许多有用的包,例如:
fmt
:格式化输入和输出。os
:提供操作系统功能,如文件操作、环境变量等。io
:提供基本的 I/O 操作接口。bufio
:提供缓冲的 I/O 操作。net
:提供网络编程功能。sync
:提供同步原语,如互斥锁、读写锁等。
6. 第三方包
可以使用
go get
命令来获取第三方包
比如在gin框架学习中引用的gin包
import "github.com/gin-gonic/gin"
7. 包的组织
- 通常,一个目录对应一个包。
- 包的名称应该与目录名称相同,除非使用
package main
。
8. 包的别名
可以为导入的包设置别名。
import (
f "fmt"
)
- 这里将
fmt
包的别名设置为f
,可以使用f.Println()
来调用fmt.Println()
。
9. 包的路径
- 包的路径是其在文件系统或远程仓库中的位置。
- 对于标准库,路径是
std
包的一部分,如fmt
包的路径是std/fmt
。 - 对于第三方包,路径是其在远程仓库中的位置,如
github.com/gin-gonic/gin
。
10. 包的版本管理
- Go 1.11 及以后的版本支持模块(module),可以使用
go.mod
文件来管理依赖的版本。 - 例如,创建一个
go.mod
文件:
module mymodule
go 1.14
require github.com/gin-gonic/gin v1.7.4
这将确保使用
github.com/gin-gonic/gin
的
v1.7.4
版本。
以下是一个完整的示例,展示了包的使用:
package main
import (
"fmt"
"mypackage"
)
func main() {
// 使用标准库的 fmt 包
fmt.Println("Hello, World!")
// 使用自定义包 mypackage
mypackage.ExportedFunction()
}
在这个示例中:
- 导入了
fmt
标准库包和mypackage
自定义包。 - 在
main
函数中,调用了fmt.Println()
和mypackage.ExportedFunction()
。
通过合理使用包,可以将代码组织得更加清晰、易于维护和复用,同时利用 Go 语言强大的标准库和丰富的第三方库资源。
四、go mod
go mod
是 Go 语言从 1.11 版本开始引入的模块管理工具,它允许开发者更好地管理项目的依赖关系,而不再依赖于
GOPATH
环境变量。
1. 初始化一个新的模块
使用
go mod init
命令来初始化一个新的模块。例如:
go mod init example.com/myproject
这将创建一个
go.mod
文件,内容如下:
module example.com/myproject
go 1.14
module
后面的部分是模块的名称,通常是一个唯一的标识符,如域名加项目名。go
后面的部分是 Go 语言的版本。
2. 依赖管理
当你导入一个新的包时,
go mod
会自动更新
go.mod
文件。例如:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
gin.Default()
}
运行
go build
或
go run
时,
go mod
会自动更新
go.mod
文件,添加所需的依赖:
module example.com/myproject
go 1.14
require github.com/gin-gonic/gin v1.7.4
3. 查看依赖关系
使用
go list -m all
命令可以查看所有的依赖关系:
go list -m all
4. 下载依赖
使用
go mod download
命令可以下载所有的依赖:
go mod download
5. 清理未使用的依赖
使用
go mod tidy
命令可以清理未使用的依赖:
go mod tidy
6. 替换依赖
如果需要替换一个依赖,可以在
go.mod
文件中使用
replace
指令。例如:
replace github.com/oldpackage => github.com/newpackage v1.2.3
7. 版本管理
go mod
会自动选择依赖的最新版本,但可以使用require
指令指定版本。例如:
require github.com/gin-gonic/gin v1.7.4
- 可以使用
@
符号指定特定的版本,如v1.7.4
、v1.7.4+incompatible
或v1.7.4-pre
。
8. 私有模块
对于私有模块,可以使用
replace
指令或环境变量
GOPRIVATE
来管理。例如:
replace private.com/myproject => /path/to/local/project
9. 构建和测试
go build
和go test
命令会自动使用go.mod
中的依赖信息。
10. 示例
以下是一个完整的
go.mod
文件示例:
module example.com/myproject
go 1.14
require (
github.com/gin-gonic/gin v1.7.4
github.com/somepackage v1.2.3
)
replace github.com/oldpackage => github.com/newpackage v1.2.3
总结
go mod init
初始化模块。go mod tidy
清理未使用的依赖。go mod download
下载依赖。go mod edit
编辑go.mod
文件。go list -m all
查看依赖。
使用
go mod
可以让 Go 项目的依赖管理更加灵活和方便,避免了
GOPATH
的限制,提高了项目的可维护性和可移植性。
版权归原作者 ●VON 所有, 如有侵权,请联系我们删除。