主题
Go语言标准库应用
课程目标
通过本课程的学习,你将掌握Go语言标准库的核心功能和应用场景,包括文件操作、网络编程、并发控制、JSON处理等常用功能,为实际项目开发打下坚实的基础。
1. 标准库概述
1.1 什么是标准库
Go语言标准库是Go语言内置的一系列包,提供了丰富的功能,包括:
- 基础功能:字符串处理、数学运算、时间操作等
- 文件操作:文件读写、目录操作等
- 网络编程:HTTP服务器、TCP/IP通信等
- 并发控制:goroutine、channel、同步原语等
- 数据序列化:JSON、XML、CSV等
- 加密解密:哈希、加密算法等
- 反射:运行时类型信息等
- 测试:单元测试、基准测试等
1.2 标准库的优势
- 稳定性:经过严格测试,稳定可靠
- 性能:经过优化,性能优异
- 一致性:API设计一致,易于学习
- 无需安装:内置在Go语言中,无需额外安装
- 文档完善:有详细的官方文档
1.3 常用标准库包
| 包名 | 功能 | 用途 |
|---|---|---|
fmt | 格式化输入输出 | 打印、格式化字符串 |
os | 操作系统功能 | 文件操作、环境变量 |
io | 输入输出接口 | 流操作、读写接口 |
net/http | HTTP服务器和客户端 | 构建Web服务、HTTP请求 |
encoding/json | JSON编解码 | 数据序列化/反序列化 |
sync | 同步原语 | 并发控制、锁、等待组 |
time | 时间操作 | 时间格式化、定时器 |
strconv | 字符串转换 | 字符串与其他类型互转 |
strings | 字符串操作 | 字符串处理、分割、替换 |
bytes | 字节操作 | 字节切片处理 |
path/filepath | 路径操作 | 文件路径处理、遍历 |
regexp | 正则表达式 | 字符串匹配、替换 |
context | 上下文管理 | 取消信号、超时控制 |
crypto | 加密功能 | 哈希、加密算法 |
testing | 测试功能 | 单元测试、基准测试 |
2. 文件操作
2.1 基本文件操作
2.1.1 创建和打开文件
go
package main
import (
"fmt"
"os"
)
func main() {
// 创建文件
file, err := os.Create("example.txt")
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer file.Close()
fmt.Println("File created successfully")
// 打开文件
file, err = os.Open("example.txt")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
fmt.Println("File opened successfully")
// 以读写模式打开文件
file, err = os.OpenFile("example.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
fmt.Println("File opened in read-write mode")
}2.1.2 读写文件
go
package main
import (
"fmt"
"os"
)
func main() {
// 写入文件
file, err := os.Create("example.txt")
if err != nil {
fmt.Println("Error creating file:", err)
return
}
// 写入字符串
n, err := file.WriteString("Hello, Go standard library!\n")
if err != nil {
fmt.Println("Error writing to file:", err)
return
}
fmt.Printf("Wrote %d bytes\n", n)
// 写入字节切片
bytes := []byte("Another line\n")
n, err = file.Write(bytes)
if err != nil {
fmt.Println("Error writing to file:", err)
return
}
fmt.Printf("Wrote %d bytes\n", n)
file.Close()
// 读取文件
file, err = os.Open("example.txt")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
// 读取全部内容
fileInfo, err := file.Stat()
if err != nil {
fmt.Println("Error getting file info:", err)
return
}
buffer := make([]byte, fileInfo.Size())
n, err = file.Read(buffer)
if err != nil {
fmt.Println("Error reading file:", err)
return
}
fmt.Printf("Read %d bytes:\n%s", n, buffer)
}2.1.3 逐行读取文件
go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("example.txt")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
lineNum := 1
for scanner.Scan() {
line := scanner.Text()
fmt.Printf("Line %d: %s\n", lineNum, line)
lineNum++
}
if err := scanner.Err(); err != nil {
fmt.Println("Error scanning file:", err)
}
}2.2 目录操作
2.2.1 创建和删除目录
go
package main
import (
"fmt"
"os"
)
func main() {
// 创建目录
err := os.Mkdir("testdir", 0755)
if err != nil {
fmt.Println("Error creating directory:", err)
return
}
fmt.Println("Directory created successfully")
// 创建多级目录
err = os.MkdirAll("parent/child/grandchild", 0755)
if err != nil {
fmt.Println("Error creating nested directories:", err)
return
}
fmt.Println("Nested directories created successfully")
// 删除目录
err = os.Remove("testdir")
if err != nil {
fmt.Println("Error removing directory:", err)
return
}
fmt.Println("Directory removed successfully")
// 删除多级目录
err = os.RemoveAll("parent")
if err != nil {
fmt.Println("Error removing nested directories:", err)
return
}
fmt.Println("Nested directories removed successfully")
}2.2.2 遍历目录
go
package main
import (
"fmt"
"os"
"path/filepath"
)
func main() {
// 遍历目录
err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
fmt.Printf("[DIR] %s\n", path)
} else {
fmt.Printf("[FILE] %s (size: %d bytes)\n", path, info.Size())
}
return nil
})
if err != nil {
fmt.Println("Error walking directory:", err)
}
}2.3 临时文件和目录
go
package main
import (
"fmt"
"os"
)
func main() {
// 创建临时文件
tempFile, err := os.CreateTemp(".", "temp-*.txt")
if err != nil {
fmt.Println("Error creating temp file:", err)
return
}
defer os.Remove(tempFile.Name()) // 清理临时文件
defer tempFile.Close()
fmt.Printf("Created temp file: %s\n", tempFile.Name())
// 写入数据
tempFile.WriteString("Temporary data")
// 创建临时目录
tempDir, err := os.MkdirTemp(".", "temp-*")
if err != nil {
fmt.Println("Error creating temp directory:", err)
return
}
defer os.RemoveAll(tempDir) // 清理临时目录
fmt.Printf("Created temp directory: %s\n", tempDir)
// 在临时目录中创建文件
tempFileInDir, err := os.Create(tempDir + "/test.txt")
if err != nil {
fmt.Println("Error creating file in temp directory:", err)
return
}
defer tempFileInDir.Close()
tempFileInDir.WriteString("File in temp directory")
fmt.Println("Created file in temp directory")
}3. 网络编程
3.1 HTTP服务器
3.1.1 基本HTTP服务器
go
package main
import (
"fmt"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, HTTP Server!\n")
fmt.Fprintf(w, "Method: %s\n", r.Method)
fmt.Fprintf(w, "URL: %s\n", r.URL.Path)
fmt.Fprintf(w, "Host: %s\n", r.Host)
}
func main() {
http.HandleFunc("/", helloHandler)
fmt.Println("Server starting on port 8080...")
if err := http.ListenAndServe(":8080", nil); err != nil {
fmt.Println("Error starting server:", err)
}
}3.1.2 自定义HTTP服务器
go
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
// 创建自定义服务器
server := &http.Server{
Addr: ":8080",
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from custom server!\n")
fmt.Fprintf(w, "Time: %s\n", time.Now().Format(time.RFC3339))
}),
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
}
fmt.Println("Custom server starting on port 8080...")
if err := server.ListenAndServe(); err != nil {
fmt.Println("Error starting server:", err)
}
}3.2 HTTP客户端
3.2.1 基本HTTP请求
go
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
// 发送GET请求
resp, err := http.Get("https://example.com")
if err != nil {
fmt.Println("Error sending request:", err)
return
}
defer resp.Body.Close()
fmt.Printf("Status: %s\n", resp.Status)
fmt.Printf("Content-Type: %s\n", resp.Header.Get("Content-Type"))
// 读取响应体
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
fmt.Printf("Response body length: %d bytes\n", len(body))
fmt.Println("First 100 bytes:", string(body[:100]))
}3.2.2 自定义HTTP客户端
go
package main
import (
"fmt"
"io/ioutil"
"net/http"
"time"
)
func main() {
// 创建自定义客户端
client := &http.Client{
Timeout: 10 * time.Second,
}
// 创建请求
req, err := http.NewRequest("GET", "https://example.com", nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
// 添加请求头
req.Header.Add("User-Agent", "Go HTTP Client")
req.Header.Add("Accept", "text/html")
// 发送请求
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
return
}
defer resp.Body.Close()
fmt.Printf("Status: %s\n", resp.Status)
// 读取响应体
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
fmt.Printf("Response body length: %d bytes\n", len(body))
}3.3 TCP服务器和客户端
3.3.1 TCP服务器
go
package main
import (
"bufio"
"fmt"
"net"
)
func handleConnection(conn net.Conn) {
defer conn.Close()
fmt.Printf("Client connected: %s\n", conn.RemoteAddr().String())
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
message := scanner.Text()
fmt.Printf("Received: %s\n", message)
// 发送响应
conn.Write([]byte("Echo: " + message + "\n"))
if message == "quit" {
break
}
}
fmt.Printf("Client disconnected: %s\n", conn.RemoteAddr().String())
}
func main() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("Error starting server:", err)
return
}
defer listener.Close()
fmt.Println("TCP server started on port 8080...")
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting connection:", err)
continue
}
go handleConnection(conn)
}
}3.3.2 TCP客户端
go
package main
import (
"bufio"
"fmt"
"net"
"os"
)
func main() {
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
fmt.Println("Error connecting to server:", err)
return
}
defer conn.Close()
fmt.Println("Connected to TCP server")
// 读取服务器响应
go func() {
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
fmt.Println("Server:", scanner.Text())
}
}()
// 发送消息
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("Enter message: ")
message, _ := reader.ReadString('\n')
conn.Write([]byte(message))
if message == "quit\n" {
break
}
}
}4. 数据序列化
4.1 JSON处理
4.1.1 JSON序列化
go
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email,omitempty"`
Address *Address `json:"address,omitempty"`
}
type Address struct {
City string `json:"city"`
Street string `json:"street"`
ZipCode string `json:"zip_code"`
}
func main() {
// 创建结构体实例
person := Person{
Name: "John Doe",
Age: 30,
Email: "john@example.com",
Address: &Address{
City: "New York",
Street: "123 Main St",
ZipCode: "10001",
},
}
// 序列化为JSON
jsonData, err := json.Marshal(person)
if err != nil {
fmt.Println("Error marshaling JSON:", err)
return
}
fmt.Println("JSON:", string(jsonData))
// 美化JSON
prettyJSON, err := json.MarshalIndent(person, "", " ")
if err != nil {
fmt.Println("Error marshaling pretty JSON:", err)
return
}
fmt.Println("Pretty JSON:", string(prettyJSON))
}4.1.2 JSON反序列化
go
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email,omitempty"`
Address *Address `json:"address,omitempty"`
}
type Address struct {
City string `json:"city"`
Street string `json:"street"`
ZipCode string `json:"zip_code"`
}
func main() {
// JSON字符串
jsonData := `{
"name": "John Doe",
"age": 30,
"email": "john@example.com",
"address": {
"city": "New York",
"street": "123 Main St",
"zip_code": "10001"
}
}`
// 反序列化为结构体
var person Person
err := json.Unmarshal([]byte(jsonData), &person)
if err != nil {
fmt.Println("Error unmarshaling JSON:", err)
return
}
fmt.Printf("Name: %s\n", person.Name)
fmt.Printf("Age: %d\n", person.Age)
fmt.Printf("Email: %s\n", person.Email)
if person.Address != nil {
fmt.Printf("Address: %s, %s, %s\n", person.Address.City, person.Address.Street, person.Address.ZipCode)
}
}4.2 XML处理
4.2.1 XML序列化
go
package main
import (
"encoding/xml"
"fmt"
)
type Person struct {
XMLName xml.Name `xml:"Person"`
Name string `xml:"Name"`
Age int `xml:"Age"`
Email string `xml:"Email,omitempty"`
Address Address `xml:"Address,omitempty"`
}
type Address struct {
City string `xml:"City"`
Street string `xml:"Street"`
ZipCode string `xml:"ZipCode"`
}
func main() {
// 创建结构体实例
person := Person{
Name: "John Doe",
Age: 30,
Email: "john@example.com",
Address: Address{
City: "New York",
Street: "123 Main St",
ZipCode: "10001",
},
}
// 序列化为XML
xmlData, err := xml.Marshal(person)
if err != nil {
fmt.Println("Error marshaling XML:", err)
return
}
fmt.Println("XML:", string(xmlData))
// 美化XML
xmlData, err = xml.MarshalIndent(person, "", " ")
if err != nil {
fmt.Println("Error marshaling pretty XML:", err)
return
}
fmt.Println("Pretty XML:", string(xmlData))
}4.2.2 XML反序列化
go
package main
import (
"encoding/xml"
"fmt"
)
type Person struct {
XMLName xml.Name `xml:"Person"`
Name string `xml:"Name"`
Age int `xml:"Age"`
Email string `xml:"Email,omitempty"`
Address Address `xml:"Address,omitempty"`
}
type Address struct {
City string `xml:"City"`
Street string `xml:"Street"`
ZipCode string `xml:"ZipCode"`
}
func main() {
// XML字符串
xmlData := `<?xml version="1.0" encoding="UTF-8"?>
<Person>
<Name>John Doe</Name>
<Age>30</Age>
<Email>john@example.com</Email>
<Address>
<City>New York</City>
<Street>123 Main St</Street>
<ZipCode>10001</ZipCode>
</Address>
</Person>`
// 反序列化为结构体
var person Person
err := xml.Unmarshal([]byte(xmlData), &person)
if err != nil {
fmt.Println("Error unmarshaling XML:", err)
return
}
fmt.Printf("Name: %s\n", person.Name)
fmt.Printf("Age: %d\n", person.Age)
fmt.Printf("Email: %s\n", person.Email)
fmt.Printf("Address: %s, %s, %s\n", person.Address.City, person.Address.Street, person.Address.ZipCode)
}5. 时间操作
5.1 时间格式化
go
package main
import (
"fmt"
"time"
)
func main() {
// 获取当前时间
now := time.Now()
fmt.Println("Current time:", now)
// 格式化时间
fmt.Println("RFC3339:", now.Format(time.RFC3339))
fmt.Println("ANSIC:", now.Format(time.ANSIC))
fmt.Println("UnixDate:", now.Format(time.UnixDate))
fmt.Println("Kitchen:", now.Format(time.Kitchen))
// 自定义格式
fmt.Println("Custom format:", now.Format("2006-01-02 15:04:05 Monday"))
// 解析时间
timeStr := "2023-12-25 10:30:00"
parsedTime, err := time.Parse("2006-01-02 15:04:05", timeStr)
if err != nil {
fmt.Println("Error parsing time:", err)
return
}
fmt.Println("Parsed time:", parsedTime)
}5.2 时间计算
go
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println("Current time:", now)
// 时间加减
oneHourLater := now.Add(1 * time.Hour)
fmt.Println("One hour later:", oneHourLater)
twoDaysAgo := now.Add(-2 * 24 * time.Hour)
fmt.Println("Two days ago:", twoDaysAgo)
// 时间比较
fmt.Println("Is oneHourLater after now?", oneHourLater.After(now))
fmt.Println("Is twoDaysAgo before now?", twoDaysAgo.Before(now))
fmt.Println("Is now equal to now?", now.Equal(now))
// 时间差
duration := oneHourLater.Sub(now)
fmt.Println("Duration:", duration)
fmt.Println("Hours:", duration.Hours())
fmt.Println("Minutes:", duration.Minutes())
fmt.Println("Seconds:", duration.Seconds())
}5.3 定时器
go
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Starting timer...")
// 一次性定时器
timer := time.NewTimer(2 * time.Second)
<-timer.C
fmt.Println("Timer fired after 2 seconds")
// 周期性定时器
ticker := time.NewTicker(1 * time.Second)
done := make(chan bool)
go func() {
for {
select {
case <-ticker.C:
fmt.Println("Ticker ticked at", time.Now().Format("15:04:05"))
case <-done:
return
}
}
}()
// 运行5秒后停止
time.Sleep(5 * time.Second)
done <- true
ticker.Stop()
fmt.Println("Ticker stopped")
}6. 字符串操作
6.1 基本字符串操作
go
package main
import (
"fmt"
"strings"
)
func main() {
str := "Hello, Go Standard Library!"
fmt.Println("Original:", str)
// 长度
fmt.Println("Length:", len(str))
// 分割
parts := strings.Split(str, " ")
fmt.Println("Split:", parts)
// 连接
joined := strings.Join(parts, "-")
fmt.Println("Joined:", joined)
// 替换
replaced := strings.ReplaceAll(str, "Go", "Golang")
fmt.Println("Replaced:", replaced)
// 大小写转换
fmt.Println("ToUpper:", strings.ToUpper(str))
fmt.Println("ToLower:", strings.ToLower(str))
// 前缀和后缀
fmt.Println("HasPrefix 'Hello':", strings.HasPrefix(str, "Hello"))
fmt.Println("HasSuffix 'Library!':", strings.HasSuffix(str, "Library!"))
// 包含
fmt.Println("Contains 'Standard':", strings.Contains(str, "Standard"))
// 查找
fmt.Println("Index of 'Go':", strings.Index(str, "Go"))
fmt.Println("LastIndex of 'o':", strings.LastIndex(str, "o"))
// 修剪
strWithSpaces := " Hello, Go! "
fmt.Println("Trimmed:", strings.TrimSpace(strWithSpaces))
}6.2 字符串转换
go
package main
import (
"fmt"
"strconv"
)
func main() {
// 整数转字符串
i := 42
str := strconv.Itoa(i)
fmt.Println("Int to string:", str)
// 字符串转整数
str = "123"
i, err := strconv.Atoi(str)
if err != nil {
fmt.Println("Error converting string to int:", err)
return
}
fmt.Println("String to int:", i)
// 浮点数转字符串
f := 3.14159
str = strconv.FormatFloat(f, 'f', 2, 64)
fmt.Println("Float to string:", str)
// 字符串转浮点数
str = "2.71828"
f, err = strconv.ParseFloat(str, 64)
if err != nil {
fmt.Println("Error converting string to float:", err)
return
}
fmt.Println("String to float:", f)
// 布尔值转字符串
b := true
str = strconv.FormatBool(b)
fmt.Println("Bool to string:", str)
// 字符串转布尔值
str = "true"
b, err = strconv.ParseBool(str)
if err != nil {
fmt.Println("Error converting string to bool:", err)
return
}
fmt.Println("String to bool:", b)
}7. 正则表达式
go
package main
import (
"fmt"
"regexp"
)
func main() {
// 编译正则表达式
re, err := regexp.Compile(`\b\w+@\w+\.\w+\b`)
if err != nil {
fmt.Println("Error compiling regex:", err)
return
}
// 测试字符串
text := "Contact us at john@example.com or support@company.org"
// 查找匹配
fmt.Println("Contains email:", re.MatchString(text))
// 查找第一个匹配
match := re.FindString(text)
fmt.Println("First match:", match)
// 查找所有匹配
matches := re.FindAllString(text, -1)
fmt.Println("All matches:", matches)
// 查找匹配位置
loc := re.FindStringIndex(text)
if loc != nil {
fmt.Printf("Match at positions %d-%d\n", loc[0], loc[1])
}
// 替换匹配
replaced := re.ReplaceAllString(text, "[email]")
fmt.Println("Replaced:", replaced)
// 自定义替换
replaced = re.ReplaceAllStringFunc(text, func(m string) string {
return "<email>" + m + "</email>"
})
fmt.Println("Custom replaced:", replaced)
}8. 上下文管理
8.1 基本用法
go
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context, id int) {
for {
select {
case <-ctx.Done():
fmt.Printf("Worker %d: Context cancelled\n", id)
return
default:
fmt.Printf("Worker %d: Working...\n", id)
time.Sleep(500 * time.Millisecond)
}
}
}
func main() {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 启动工作协程
for i := 1; i <= 3; i++ {
go worker(ctx, i)
}
// 运行3秒后取消
time.Sleep(3 * time.Second)
fmt.Println("Cancelling context...")
cancel()
// 等待工作协程退出
time.Sleep(1 * time.Second)
fmt.Println("Main function exiting")
}8.2 超时控制
go
package main
import (
"context"
"fmt"
"time"
)
func longRunningTask(ctx context.Context) error {
fmt.Println("Starting long running task...")
// 模拟长时间操作
for i := 1; i <= 10; i++ {
select {
case <-ctx.Done():
return ctx.Err()
default:
fmt.Printf("Task step %d...\n", i)
time.Sleep(1 * time.Second)
}
}
fmt.Println("Task completed successfully")
return nil
}
func main() {
// 创建带超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 执行任务
err := longRunningTask(ctx)
if err != nil {
fmt.Printf("Task error: %v\n", err)
}
fmt.Println("Main function exiting")
}8.3 传递值
go
package main
import (
"context"
"fmt"
)
func handler(ctx context.Context) {
// 获取上下文值
userID, ok := ctx.Value("userID").(string)
if ok {
fmt.Printf("Handler: User ID is %s\n", userID)
}
// 获取嵌套上下文值
requestID, ok := ctx.Value("requestID").(string)
if ok {
fmt.Printf("Handler: Request ID is %s\n", requestID)
}
}
func main() {
// 创建基础上下文
ctx := context.Background()
// 添加值
ctx = context.WithValue(ctx, "userID", "12345")
ctx = context.WithValue(ctx, "requestID", "abc-123")
// 调用处理函数
handler(ctx)
}9. 加密功能
9.1 哈希函数
go
package main
import (
"crypto/md5"
"crypto/sha256"
"fmt"
"hex"
)
func main() {
data := "Hello, Go!"
// MD5哈希
md5Hash := md5.Sum([]byte(data))
fmt.Printf("MD5: %s\n", hex.EncodeToString(md5Hash[:]))
// SHA-256哈希
sha256Hash := sha256.Sum256([]byte(data))
fmt.Printf("SHA-256: %s\n", hex.EncodeToString(sha256Hash[:]))
}9.2 随机数生成
go
package main
import (
"crypto/rand"
"fmt"
"math/big"
)
func main() {
// 生成随机整数
n, err := rand.Int(rand.Reader, big.NewInt(100))
if err != nil {
fmt.Println("Error generating random number:", err)
return
}
fmt.Println("Random integer (0-99):", n.Int64())
// 生成随机字节
randomBytes := make([]byte, 16)
_, err = rand.Read(randomBytes)
if err != nil {
fmt.Println("Error generating random bytes:", err)
return
}
fmt.Printf("Random bytes: %x\n", randomBytes)
}10. 测试功能
10.1 单元测试
10.1.1 基本测试
go
// math.go
package math
func Add(a, b int) int {
return a + b
}
func Subtract(a, b int) int {
return a - b
}
func Multiply(a, b int) int {
return a * b
}
func Divide(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
// math_test.go
package math
import (
"testing"
)
func TestAdd(t *testing.T) {
tests := []struct {
a, b, expected int
}{
{1, 2, 3},
{0, 0, 0},
{-1, 1, 0},
{-2, -3, -5},
}
for _, tt := range tests {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected)
}
}
}
func TestSubtract(t *testing.T) {
result := Subtract(5, 3)
if result != 2 {
t.Errorf("Subtract(5, 3) = %d; want 2", result)
}
}
func TestMultiply(t *testing.T) {
result := Multiply(4, 5)
if result != 20 {
t.Errorf("Multiply(4, 5) = %d; want 20", result)
}
}
func TestDivide(t *testing.T) {
// 正常情况
result, err := Divide(10, 2)
if err != nil {
t.Errorf("Divide(10, 2) returned error: %v", err)
}
if result != 5 {
t.Errorf("Divide(10, 2) = %d; want 5", result)
}
// 除以零
_, err = Divide(10, 0)
if err == nil {
t.Error("Divide(10, 0) should have returned an error")
}
}10.1.2 运行测试
bash
go test ./math
go test -v ./math # 详细输出
go test -run TestAdd ./math # 只运行特定测试
go test ./... # 运行所有测试10.2 基准测试
go
// math_benchmark_test.go
package math
import (
"testing"
)
func BenchmarkAdd(b *testing.B) {
// 重置计时器
b.ResetTimer()
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}
func BenchmarkMultiply(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
Multiply(4, 5)
}
}运行基准测试:
bash
go test -bench=. ./math
go test -bench=BenchmarkAdd ./math # 只运行特定基准测试
go test -bench=. -benchmem ./math # 包含内存分配信息11. 综合实战案例
11.1 简单的Web服务器
go
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"path/filepath"
"strconv"
"sync"
"time"
)
// 存储结构
type Item struct {
ID string `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
CreatedAt time.Time `json:"created_at"`
}
// 内存存储
var (
items = make(map[string]Item)
itemsMutex sync.RWMutex
nextID = 1
)
// 处理函数
func createItem(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var item Item
if err := json.NewDecoder(r.Body).Decode(&item); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// 生成ID
itemsMutex.Lock()
item.ID = strconv.Itoa(nextID)
nextID++
item.CreatedAt = time.Now()
items[item.ID] = item
itemsMutex.Unlock()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(item)
}
func getItems(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
itemsMutex.RLock()
defer itemsMutex.RUnlock()
// 转换为切片
itemList := make([]Item, 0, len(items))
for _, item := range items {
itemList = append(itemList, item)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(itemList)
}
func getItem(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
id := filepath.Base(r.URL.Path)
itemsMutex.RLock()
item, exists := items[id]
itemsMutex.RUnlock()
if !exists {
http.Error(w, "Item not found", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(item)
}
func updateItem(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPut {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
id := filepath.Base(r.URL.Path)
itemsMutex.Lock()
defer itemsMutex.Unlock()
item, exists := items[id]
if !exists {
http.Error(w, "Item not found", http.StatusNotFound)
return
}
var updatedItem Item
if err := json.NewDecoder(r.Body).Decode(&updatedItem); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// 更新字段
item.Name = updatedItem.Name
item.Price = updatedItem.Price
items[id] = item
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(item)
}
func deleteItem(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodDelete {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
id := filepath.Base(r.URL.Path)
itemsMutex.Lock()
defer itemsMutex.Unlock()
if _, exists := items[id]; !exists {
http.Error(w, "Item not found", http.StatusNotFound)
return
}
delete(items, id)
w.WriteHeader(http.StatusNoContent)
}
func main() {
// 路由
http.HandleFunc("/items", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
getItems(w, r)
case http.MethodPost:
createItem(w, r)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
})
http.HandleFunc("/items/", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
getItem(w, r)
case http.MethodPut:
updateItem(w, r)
case http.MethodDelete:
deleteItem(w, r)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
})
// 静态文件
http.Handle("/", http.FileServer(http.Dir("./static")))
// 获取端口
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
fmt.Printf("Server starting on port %s...\n", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}11.2 命令行工具
go
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"flag"
)
func main() {
// 命令行参数
var (
extension = flag.String("ext", ".txt", "File extension to search for")
recursive = flag.Bool("r", false, "Recursive search")
count = flag.Bool("c", false, "Count files only")
output = flag.String("o", "", "Output file")
)
flag.Parse()
// 获取搜索目录
dirs := flag.Args()
if len(dirs) == 0 {
dirs = []string{"."}
}
// 搜索文件
var foundFiles []string
for _, dir := range dirs {
files, err := findFiles(dir, *extension, *recursive)
if err != nil {
fmt.Fprintf(os.Stderr, "Error searching %s: %v\n", dir, err)
continue
}
foundFiles = append(foundFiles, files...)
}
// 处理结果
if *count {
fmt.Printf("Found %d files with extension %s\n", len(foundFiles), *extension)
} else {
if *output != "" {
// 写入文件
err := writeResults(*output, foundFiles)
if err != nil {
fmt.Fprintf(os.Stderr, "Error writing output: %v\n", err)
return
}
fmt.Printf("Results written to %s\n", *output)
} else {
// 打印到控制台
for _, file := range foundFiles {
fmt.Println(file)
}
fmt.Printf("Total: %d files\n", len(foundFiles))
}
}
}
func findFiles(dir, ext string, recursive bool) ([]string, error) {
var files []string
if recursive {
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && strings.HasSuffix(path, ext) {
files = append(files, path)
}
return nil
})
if err != nil {
return nil, err
}
} else {
entries, err := ioutil.ReadDir(dir)
if err != nil {
return nil, err
}
for _, entry := range entries {
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ext) {
files = append(files, filepath.Join(dir, entry.Name()))
}
}
}
return files, nil
}
func writeResults(filename string, files []string) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
writer := bufio.NewWriter(file)
defer writer.Flush()
for _, f := range files {
fmt.Fprintln(writer, f)
}
return nil
}12. 最佳实践
12.1 代码组织
- 模块化设计:将功能分解为独立的包和模块
- 清晰的命名:使用有意义的包名、函数名和变量名
- 错误处理:正确处理和传递错误
- 文档注释:为公共API添加详细的文档注释
- 测试覆盖:为代码编写单元测试
12.2 性能优化
- 避免不必要的分配:重用变量和缓冲区
- 使用适当的数据结构:根据场景选择合适的数据结构
- 减少锁竞争:使用细粒度锁或无锁数据结构
- I/O优化:使用缓冲I/O,批量操作
- 避免反射:反射操作较慢,尽量避免使用
12.3 安全性
- 输入验证:验证所有用户输入
- 错误处理:不要在错误消息中泄露敏感信息
- 资源管理:确保资源正确释放
- 并发安全:确保并发操作的安全性
- 避免缓冲区溢出:正确处理缓冲区大小
12.4 标准库使用技巧
- 查阅文档:熟悉标准库的功能和用法
- 使用上下文:在需要取消和超时控制的场景中使用context
- 使用同步原语:正确使用sync包中的同步原语
- 序列化选择:根据场景选择合适的序列化格式
- 网络编程:使用net/http包构建Web服务
13. 总结
本课程介绍了Go语言标准库的核心功能和应用场景,包括:
- 文件操作:创建、读写、遍历文件和目录
- 网络编程:HTTP服务器和客户端、TCP通信
- 数据序列化:JSON、XML处理
- 时间操作:时间格式化、计算、定时器
- 字符串操作:字符串处理、转换
- 正则表达式:字符串匹配、替换
- 上下文管理:取消信号、超时控制、值传递
- 加密功能:哈希函数、随机数生成
- 测试功能:单元测试、基准测试
- 综合实战:Web服务器、命令行工具
通过本课程的学习,你已经掌握了Go语言标准库的常用功能,可以在实际项目中灵活应用这些功能来构建高效、可靠的Go应用程序。标准库是Go语言的强大武器,熟练掌握它将大大提高你的开发效率和代码质量。