Go语言笔记
第三方包推荐
- 解决浮点数精度损失问题:https://pkg.go.dev/github.com/shopspring/decimal
- 快速从JSON文档中获取值:https://pkg.go.dev/github.com/tidwall/gjson
- proto:https://pkg.go.dev/google.golang.org/protobuf
- 自动加载env文件: https://pkg.go.dev/github.com/joho/godotenv/autoload
- ws:https://pkg.go.dev/github.com/gorilla/websocket
- jwt:https://pkg.go.dev/github.com/golang-jwt/jwt/v5
- bcrypt:https://pkg.go.dev/golang.org/x/crypto/bcrypt
- redis:https://pkg.go.dev/github.com/redis/go-redis/v9
- gorm:https://pkg.go.dev/gorm.io/gorm
命令行
go env
查看全局环境变量和配置
go env
go mod
使用go mod来管理项目,使用以下命令来初始化项目
go mod init project-name
如果缺少依赖可以执行以下命令
go mod tidy
go run
运行main.go
go run main.go
go build
构建当前系统对应的可执行文件(go env里的配置项)
go build
也可以指定系统和架构,以下是常见的系统(GOOS)和 架构(GOARCH):
系统(GOOS):
linux
(linux系统)windows
(windows系统)darwin
(mac系统)android
(android系统)
架构(GOARCH):
amd64
(64 位 x86 架构,如 Intel/AMD 64 位 CPU)386
(32 位 x86 架构)arm
(ARM 架构,如树莓派、嵌入式设备)arm64
(64 位 ARM 架构)
常见组合:
场景 | GOOS | GOARCH |
---|---|---|
Windows 64 位 | windows | amd64 |
Linux 32 位 | linux | 386 |
macOS(Intel) | darwin | amd64 |
macOS(Apple Silicon) | darwin | arm64 |
Linux ARM(树莓派) | linux | arm |
Android ARM64 | android | arm64 |
修改方式:
例如将系统设置为 linux
,架构为 amd64
- 全局修改
go env -w GOOS=linux
go env -w GOARCH=amd64
- 一次性修改(不改变全局)
- PowerShell:
$env:GOOS="linux"; $env:GOARCH="amd64"; go build
- CMD:
set GOOS=linux&set GOARCH=amd64&go build
指定名称:
参数 -o
,用法
go build -o myapp
注意:在windows上需要加 .exe
后缀,如:
go build -o myapp.exe
跨平台构建时的命名技巧:
# Linux/macOS
# GOOS=linux GOARCH=amd64
go build -o myapp-linux-amd64
# Windows
# GOOS=windows GOARCH=amd64
go build -o myapp-windows-amd64.exe
os.OpenFile()方法
1. name:文件路径
直接传入文件路径字符串,且需要确保程序对目标目录有读写权限
可以是相对路径(如 "./data.txt")或绝对路径(如 "/tmp/file.log")
// 当前目录下的文件
path := "data.txt"
// 绝对路径(注意操作系统的路径分隔符差异)
path := "C:\\tmp\\file.log" // Windows
path := "/tmp/file.log" // Unix-like
2. flag:打开模式和行为
这些标志是通过按位或操作 |
组合使用的。以下是所有可用的标志及其作用:
1. 基本打开模式(必选其一)
标志 | 值 | 说明 |
---|---|---|
os.O_RDONLY | 0 | 只读打开文件 |
os.O_WRONLY | 1 | 只写打开文件 |
os.O_RDWR | 2 | 读写打开文件 |
2. 文件操作行为(可选组合)
标志 | 值 | 说明 |
---|---|---|
os.O_APPEND | 1024 | 追加模式(写入时定位到文件末尾) |
os.O_CREATE | 64 | 如果文件不存在则创建 |
os.O_EXCL | 128 | 与 O_CREATE 共用时,文件必须不存在(否则报错) |
os.O_SYNC | 4096 | 同步I/O(写入直接刷盘,保证数据持久化) |
os.O_TRUNC | 512 | 打开时清空文件(如果文件已存在且可写) |
os.O_DSYNC | 2048 | 类似 O_SYNC ,但只同步数据(不同步元数据) |
os.O_TEMPORARY | 256 | (Windows特有)文件为临时文件,程序退出后自动删除 |
3. 常见组合示例
- 只读打开(文件必须存在)
file, err := os.OpenFile("file.txt", os.O_RDONLY, 0)
- 读写打开,不存在则创建
file, err := os.OpenFile("file.txt", os.O_RDWR|os.O_CREATE, 0644)
- 只写追加(日志文件常用)
file, err := os.OpenFile("log.txt", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
- 清空文件后写入
file, err := os.OpenFile("data.bin", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
- 原子性创建(文件必须不存在)
file, err := os.OpenFile("lockfile", os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644)
if err != nil {
fmt.Println("文件已存在,无法重复创建")
}
3. perm:文件权限
类型: os.FileMode
(本质是 uint32)
写法: 使用 八进制数字 表示权限位
常用权限值:
权限值 | 说明 |
---|---|
0644 | 用户可读写,其他用户只读 |
0666 | 所有用户可读写 |
0600 | 仅用户可读写 |
0755 | 用户可读写执行,其他用户可读执行 |
提示
更多权限相关可以查看:Linux文件权限问题
特殊注意事项
Windows系统:
- 权限参数可能被忽略(尤其是执行权限)
- 但建议仍按规范填写(如
0644
)
权限生效时机:
- 仅在创建新文件时生效(
O_CREATE
触发时) - 对已存在文件不会修改权限
- 仅在创建新文件时生效(
错误处理:
file, err := os.OpenFile("data.txt", os.O_RDWR, 0644)
if err != nil {
// 处理错误(如无权限、路径不存在等)
log.Fatal(err)
}
defer file.Close() // 确保关闭文件
读取文件
1. 全部读取(小文件)
推荐方式: os.ReadFile
适用场景: 配置文件、小型文本或二进制文件(<10MB)
优点: 代码简洁,性能高
content, err := os.ReadFile("file.txt")
if err != nil {
return err
}
fmt.Println(string(content))
2. 逐行读取文本文件
推荐方式: bufio.Scanner
适用场景: 日志、CSV、配置文件等文本文件
优点: 自动处理换行符,内存高效
file, err := os.Open("file.txt")
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
return err
}
优化:
调整缓冲区大小(默认 4KB,最大 64KB):
scanner.Buffer(make([]byte, 1024*1024), 1024*1024) // 1MB 缓冲区
适用于超大文件,避免内存问题
3. 分块读取(大文件或二进制文件)
推荐方式: bufio.Reader + 自定义缓冲区
适用场景: 大文件、二进制数据(如图片、视频)
优点: 灵活控制内存,减少系统调用
file, err := os.Open("largefile.bin")
if err != nil {
return err
}
defer file.Close()
reader := bufio.NewReader(file)
buf := make([]byte, 4096) // 4KB 块
for {
n, err := reader.Read(buf)
if err == io.EOF {
break
}
if err != nil {
return err
}
process(buf[:n]) // 处理每块数据
}
注意: 适合流式处理,避免内存爆炸
4. 超大型文件(GB级)
推荐方式: 内存映射(mmap)
适用场景: 日志分析、数据库文件等
示例(第三方库):
file, _ := os.Open("hugefile.bin")
defer file.Close()
mmapData, _ := mmap.Map(file, mmap.RDONLY, 0)
defer mmapData.Unmap()
fmt.Println(string(mmapData))
优点: 零拷贝,性能极高
读取文件方式总结
场景 | 推荐方式 | 内存占用 |
---|---|---|
小文件 | os.ReadFile | 高 |
逐行文本 | bufio.Scanner | 低 |
大文件/二进制 | bufio.Reader | 中 |
超大文件 | mmap (第三方) | 最低 |
写入文件
1. 一次性写入(小文件推荐)
使用 os.WriteFile
适用于小文件,会覆盖原有内容:
content := []byte("Hello, 世界!")
err := os.WriteFile("./file.txt", content, 0644)
if err != nil {
log.Fatal(err)
}
0644 是文件权限(用户可读写,其他用户只读),windows上不存在这个权限
如果文件不存在会自动创建
2. 追加写入
使用 os.OpenFile
带 os.O_APPEND
标志:
file, err := os.OpenFile("file.txt", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
_, err = file.Write([]byte("追加的内容\n"))
if err != nil {
log.Fatal(err)
}
3. 缓冲写入(高性能写入)
使用 bufio.Writer
,适合频繁写入:
file, err := os.Create("file.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
writer := bufio.NewWriter(file)
_, err = writer.WriteString("缓冲写入1\n")
if err != nil {
log.Fatal(err)
}
_, err = writer.WriteString("缓冲写入2\n")
if err != nil {
log.Fatal(err)
}
writer.Flush() // 必须调用,确保数据写入磁盘
Protobuf
1. 安装 protobuf 编译器 protoc
# macOS (Homebrew)
brew install protobuf
# Ubuntu/Debian
sudo apt update && sudo apt install -y protobuf-compiler
# Windows (WSL 或安装 Chocolatey)
choco install protoc
检查是否安装成功:
protoc --version
输出:
libprotoc 31.1
2. 安装 protoc-gen-go 插件
用于编译proto文件为go代码,是一个可执行文件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
# 旧版(已废弃)
github.com/golang/protobuf/protoc-gen-go
3. proto示例文件
// hello.proto
syntax = "proto3"; // 使用proto3语法
package example;
option go_package = "your-project/proto;example";
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
4. 编译proto文件
protoc --go_out=. --go_opt=paths=source_relative hello.proto
会多出一个 hello.pb.go
的文件
5. 使用proto
package main
import (
"fmt"
hello "prototest/proto"
"google.golang.org/protobuf/proto" // 默认不存在,需要tidy
)
func main() {
// 创建请求实例
req := &hello.HelloRequest{
Name: "Alice",
Age: 18,
}
fmt.Println("Request:", req)
// 序列化
data, _ := proto.Marshal(req)
fmt.Println("Serialized Data:", string(data))
// 反序列化
var res hello.HelloRequest
_ = proto.Unmarshal(data, &res)
fmt.Println("Deserialized Request:", &res)
}
执行 go mod tidy
自动下载依赖包,一共有两个包,建议使用新版
- 新版:
google.golang.org/protobuf
- 旧版:
github.com/golang/protobuf
(已废弃)
执行main方法,打印出以下结果:
Request: name:"Alice" age:18
Serialized Data:
Alice
Deserialized Request: name:"Alice" age:18
序列化后有加密,有些字符显示不出来,正常现象