Package与模块化管理
在 Go 中,包(Package) 是代码组织和复用的基本单位。而自 Go 1.11 版本引入的 Go Modules (go mod
),已成为官方推荐的依赖管理系统,它使得项目可以独立于 $GOPATH
存在,并能精确控制依赖版本。
包的基本概念
- 包声明: 每个
.go
文件的第一行都必须是package <包名>
。 - main 包: 一个可执行程序的入口点必须是
main
包,且包含一个main()
函数。 - 导入包: 使用
import
关键字导入其他包。可以为导入的包设置别名,或使用.
将其成员直接引入当前命名空间(不推荐)。goimport ( "fmt" "os" fm "fmt" // 使用别名 fm . "strings" // 不推荐,直接使用 strings 包的函数,如 ToUpper() )
- 可见性: 标识符(变量、常量、类型、函数等)的可见性由其首字母的大小写决定。大写字母开头的标识符是公开的(Exported),可以被其他包访问;小写字母开头的是私有的(Unexported),仅在包内可见。
使用 go mod
管理项目
go mod
让依赖管理变得前所未有的简单。
核心命令:
go mod init <module_path>
: 在当前目录下初始化一个新的模块。<module_path>
通常是代码仓库的路径,如github.com/your-username/my-project
。此命令会生成一个go.mod
文件。bashmkdir myproject cd myproject go mod init github.com/user/myproject
go get <package_path>[@version]
: 添加新的依赖或更新现有依赖。例如,获取gorilla/mux
的最新版本。bashgo get github.com/gorilla/mux
go mod tidy
: 最常用的命令之一。它会分析代码中的import
语句,移除go.mod
文件中不再使用的依赖,并添加代码中需要但尚未记录的依赖。go build
,go test
,go run
: 这些命令在模块模式下会自动下载和管理所需的依赖。
go.mod
文件:
这是模块的核心,记录了模块路径、Go 版本以及所有直接和间接依赖及其版本。
go.sum
文件:
自动生成,记录了每个依赖包在特定版本的加密哈希值,用于保证依赖的完整性和安全性,防止下载被篡改的包。应将其提交到版本控制系统中。
文件读写(I/O 操作)
Go 的 os
和 io
包提供了强大而简洁的文件操作接口。
读取文件
最常用、最安全的方式是使用 os.ReadFile
,它能一次性读取整个文件的内容。
package main
import (
"fmt"
"os"
)
func main() {
// 读取整个文件
content, err := os.ReadFile("example.txt")
if err != nil {
fmt.Println("Error reading file:", err)
return
}
fmt.Println(string(content))
}
对于大文件,应使用流式读取,以避免一次性加载过多内容到内存。
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("large_file.txt")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close() // 确保文件在函数结束时关闭
scanner := bufio.NewScanner(file)
// 逐行读取
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("Error scanning file:", err)
}
}
写入文件
使用 os.WriteFile
可以方便地将字节切片写入文件。该函数会自动处理文件的创建和权限设置。
package main
import (
"fmt"
"os"
)
func main() {
data := []byte("Hello, Golang I/O!")
// 写入文件,如果文件不存在则创建。0666 是文件权限。
err := os.WriteFile("output.txt", data, 0666)
if err != nil {
fmt.Println("Error writing to file:", err)
}
}
要追加内容到文件,需要使用更底层的 os.OpenFile
。
package main
import (
"fmt"
"os"
)
func main() {
// os.O_APPEND: 追加模式, os.O_CREATE: 文件不存在则创建, os.O_WRONLY: 只写模式
file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
if _, err := file.WriteString("This is a new log entry.\n"); err != nil {
fmt.Println("Error writing to file:", err)
}
}
JSON 操作
encoding/json
包是 Go 处理 JSON 的标准库,支持将 Go 的结构体与 JSON 数据进行相互转换(序列化和反序列化)。
序列化 (Marshal)
序列化 (Marshal): Go 结构体 -> JSON
将一个 Go 的数据结构(通常是 struct
或 map
)转换为 JSON 字节流。
关键点:结构体的字段必须是**首字母大写(公开的)**才能被 json
包访问和序列化。可以使用 json
标签自定义字段在 JSON 中的名称。
package main
import (
"encoding/json"
"fmt"
"os"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"-"` // "-" 表示此字段不参与序列化
}
func main() {
user := User{ID: 1, Name: "Alice", Email: "[email protected]", Password: "secret"}
// 序列化
jsonData, err := json.MarshalIndent(user, "", " ") // MarshalIndent 生成带缩进的格式化 JSON
if err != nil {
fmt.Println("Error marshaling to JSON:", err)
return
}
// 打印或写入文件
fmt.Println(string(jsonData))
os.WriteFile("user.json", jsonData, 0666)
}
反序列化 (Unmarshal)
反序列化 (Unmarshal): JSON -> Go 结构体 :将 JSON 字节流解析到 Go 的数据结构中。
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
jsonData := []byte(`{"id": 2, "name": "Bob", "email": "[email protected]"}`)
var user User
// 反序列化
err := json.Unmarshal(jsonData, &user) // 第二个参数必须是指针
if err != nil {
fmt.Println("Error unmarshaling JSON:", err)
return
}
fmt.Printf("User: %+v\n", user)
}
表格处理(以 CSV 为例)
处理表格数据(如 CSV, Excel)是数据处理的常见任务。Go 的 encoding/csv
包提供了对 CSV 文件的强大支持。
读取 CSV 文件
package main
import (
"encoding/csv"
"fmt"
"os"
)
func main() {
file, err := os.Open("data.csv")
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close()
reader := csv.NewReader(file)
// 可以设置分隔符,默认为逗号 ','
// reader.Comma = ';'
// 读取所有记录
records, err := reader.ReadAll()
if err != nil {
fmt.Println("Error reading CSV:", err)
return
}
for i, record := range records {
fmt.Printf("Row %d: %v\n", i+1, record)
}
}
写入 CSV 文件
package main
import (
"encoding/csv"
"fmt"
"os"
)
func main() {
file, err := os.Create("output.csv")
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close()
writer := csv.NewWriter(file)
defer writer.Flush() // 确保所有缓冲的数据都写入文件
// 待写入的数据
data := [][]string{
{"Name", "Age", "City"},
{"Alice", "30", "New York"},
{"Bob", "25", "London"},
}
for _, record := range data {
err := writer.Write(record)
if err != nil {
fmt.Println("Error writing record to csv:", err)
}
}
}
通过 writer.WriteAll(data)
可以一次性写入所有数据,更为便捷。