phase3-practice.md 70 KB

第三阶段:Go语言实战模块

学习目标

  • 掌握Go语言在实际开发中的应用
  • 理解文件操作、网络编程、数据库连接
  • 能够开发完整的应用程序
  • 掌握测试和调试技巧

详细学习内容

3.1 文件I/O操作

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
    "path/filepath"
    "strings"
)

// 文件读写
func readFile(filename string) (string, error) {
    data, err := os.ReadFile(filename)
    if err != nil {
        return "", err
    }
    return string(data), nil
}

// 按行读取文件
func readFileByLines(filename string) ([]string, error) {
    file, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    
    var lines []string
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        lines = append(lines, scanner.Text())
    }
    
    if err := scanner.Err(); err != nil {
        return nil, err
    }
    
    return lines, nil
}

// 写入文件
func writeFile(filename, content string) error {
    return os.WriteFile(filename, []byte(content), 0644)
}

// 追加内容到文件
func appendToFile(filename, content string) error {
    file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        return err
    }
    defer file.Close()
    
    _, err = file.WriteString(content)
    return err
}

// 目录操作
func listFiles(dir string) ([]string, error) {
    entries, err := os.ReadDir(dir)
    if err != nil {
        return nil, err
    }
    
    var files []string
    for _, entry := range entries {
        files = append(files, entry.Name())
    }
    return files, nil
}

// 递归列出所有文件
func listAllFiles(dir string) ([]string, error) {
    var files []string
    
    err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        if !info.IsDir() {
            files = append(files, path)
        }
        return nil
    })
    
    return files, err
}

// 复制文件
func copyFile(src, dst string) error {
    sourceFile, err := os.Open(src)
    if err != nil {
        return err
    }
    defer sourceFile.Close()
    
    destFile, err := os.Create(dst)
    if err != nil {
        return err
    }
    defer destFile.Close()
    
    _, err = io.Copy(destFile, sourceFile)
    return err
}

// 获取文件信息
func getFileInfo(filename string) (os.FileInfo, error) {
    return os.Stat(filename)
}

// 创建目录
func createDir(dirName string) error {
    return os.MkdirAll(dirName, 0755)
}

func main() {
    // 创建测试目录和文件
    testDir := "./test_files"
    createDir(testDir)
    
    // 写入测试文件
    testFile := filepath.Join(testDir, "test.txt")
    content := "这是测试文件\n包含多行内容\n用于演示文件操作"
    err := writeFile(testFile, content)
    if err != nil {
        fmt.Printf("写入文件错误: %v\n", err)
        return
    }
    
    // 读取文件
    data, err := readFile(testFile)
    if err != nil {
        fmt.Printf("读取文件错误: %v\n", err)
        return
    }
    fmt.Printf("文件内容:\n%s\n", data)
    
    // 按行读取
    lines, err := readFileByLines(testFile)
    if err != nil {
        fmt.Printf("按行读取错误: %v\n", err)
        return
    }
    fmt.Printf("文件行数: %d\n", len(lines))
    
    // 追加内容
    err = appendToFile(testFile, "\n追加的新行")
    if err != nil {
        fmt.Printf("追加内容错误: %v\n", err)
        return
    }
    
    // 列出目录中的文件
    files, err := listFiles(testDir)
    if err != nil {
        fmt.Printf("列出文件错误: %v\n", err)
        return
    }
    fmt.Printf("目录中的文件: %v\n", files)
    
    // 获取文件信息
    info, err := getFileInfo(testFile)
    if err != nil {
        fmt.Printf("获取文件信息错误: %v\n", err)
        return
    }
    fmt.Printf("文件大小: %d 字节\n", info.Size())
    fmt.Printf("文件修改时间: %v\n", info.ModTime())
    
    // 复制文件
    copyFile := filepath.Join(testDir, "test_copy.txt")
    err = copyFile(testFile, copyFile)
    if err != nil {
        fmt.Printf("复制文件错误: %v\n", err)
        return
    }
    fmt.Printf("文件已复制到: %s\n", copyFile)
    
    // 递归列出所有文件
    allFiles, err := listAllFiles(".")
    if err != nil {
        fmt.Printf("递归列出文件错误: %v\n", err)
        return
    }
    fmt.Printf("当前目录及子目录中的所有文件数量: %d\n", len(allFiles))
    
    // 清理测试文件
    os.RemoveAll(testDir)
    fmt.Println("测试完成,已清理测试文件")
}

3.2 网络编程

package main

import (
    "bufio"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net"
    "net/http"
    "os"
    "strings"
    "time"
)

// HTTP客户端
func httpGet(url string) (string, error) {
    resp, err := http.Get(url)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return "", err
    }
    return string(body), nil
}

// 带超时的HTTP客户端
func httpGetWithTimeout(url string, timeout time.Duration) (string, error) {
    client := http.Client{
        Timeout: timeout,
    }
    
    resp, err := client.Get(url)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return "", err
    }
    return string(body), nil
}

// POST请求
func httpPost(url string, data interface{}) (string, error) {
    jsonData, err := json.Marshal(data)
    if err != nil {
        return "", err
    }
    
    resp, err := http.Post(url, "application/json", strings.NewReader(string(jsonData)))
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return "", err
    }
    return string(body), nil
}

// HTTP服务器
func startHTTPServer() {
    // 静态文件服务
    fs := http.FileServer(http.Dir("./static"))
    http.Handle("/static/", http.StripPrefix("/static/", fs))
    
    // API路由
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World! 当前时间: %s", time.Now().Format("2006-01-02 15:04:05"))
    })
    
    http.HandleFunc("/api/hello", func(w http.ResponseWriter, r *http.Request) {
        name := r.URL.Query().Get("name")
        if name == "" {
            name = "Guest"
        }
        
        response := map[string]string{
            "message": fmt.Sprintf("Hello, %s!", name),
            "time":    time.Now().Format(time.RFC3339),
        }
        
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(response)
    })
    
    http.HandleFunc("/api/data", func(w http.ResponseWriter, r *http.Request) {
        if r.Method != http.MethodPost {
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
            return
        }
        
        var data map[string]interface{}
        if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
            http.Error(w, "Invalid JSON", http.StatusBadRequest)
            return
        }
        
        // 处理数据
        data["processed"] = true
        data["timestamp"] = time.Now().Unix()
        
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(data)
    })
    
    fmt.Println("HTTP服务器启动,监听端口: 8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

// TCP连接处理
func handleConnection(conn net.Conn) {
    defer conn.Close()
    
    // 设置连接超时
    conn.SetDeadline(time.Now().Add(30 * time.Second))
    
    // 发送欢迎消息
    conn.Write([]byte("欢迎连接TCP服务器!\n"))
    
    scanner := bufio.NewScanner(conn)
    for scanner.Scan() {
        text := scanner.Text()
        if text == "quit" {
            break
        }
        
        // 处理命令
        response := processCommand(text)
        conn.Write([]byte(response + "\n"))
    }
    
    if err := scanner.Err(); err != nil {
        fmt.Printf("连接错误: %v\n", err)
    }
    
    fmt.Printf("客户端 %s 断开连接\n", conn.RemoteAddr())
}

// 处理TCP命令
func processCommand(command string) string {
    parts := strings.Fields(command)
    if len(parts) == 0 {
        return "无效命令"
    }
    
    switch parts[0] {
    case "time":
        return fmt.Sprintf("当前时间: %s", time.Now().Format("2006-01-02 15:04:05"))
    case "echo":
        if len(parts) > 1 {
            return strings.Join(parts[1:], " ")
        }
        return "echo需要参数"
    case "reverse":
        if len(parts) > 1 {
            text := strings.Join(parts[1:], " ")
            runes := []rune(text)
            for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
                runes[i], runes[j] = runes[j], runes[i]
            }
            return string(runes)
        }
        return "reverse需要参数"
    default:
        return fmt.Sprintf("未知命令: %s", parts[0])
    }
}

// TCP服务器
func startTCPServer() {
    ln, err := net.Listen("tcp", ":8081")
    if err != nil {
        log.Fatal(err)
    }
    defer ln.Close()
    
    fmt.Println("TCP服务器启动,监听端口: 8081")
    
    for {
        conn, err := ln.Accept()
        if err != nil {
            log.Println(err)
            continue
        }
        
        fmt.Printf("新客户端连接: %s\n", conn.RemoteAddr())
        go handleConnection(conn)
    }
}

// TCP客户端
func startTCPClient(serverAddr string) {
    conn, err := net.Dial("tcp", serverAddr)
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()
    
    fmt.Printf("已连接到TCP服务器: %s\n", serverAddr)
    
    // 读取欢迎消息
    scanner := bufio.NewScanner(conn)
    if scanner.Scan() {
        fmt.Println("服务器:", scanner.Text())
    }
    
    // 发送命令
    reader := bufio.NewReader(os.Stdin)
    for {
        fmt.Print("输入命令 (quit退出): ")
        text, _ := reader.ReadString('\n')
        text = strings.TrimSpace(text)
        
        if text == "quit" {
            break
        }
        
        // 发送命令
        fmt.Fprintln(conn, text)
        
        // 读取响应
        if scanner.Scan() {
            fmt.Println("服务器:", scanner.Text())
        }
    }
}

func main() {
    if len(os.Args) < 2 {
        fmt.Println("使用方法:")
        fmt.Println("  HTTP服务器: go run network.go http")
        fmt.Println("  TCP服务器: go run network.go tcp-server")
        fmt.Println("  TCP客户端: go run network.go tcp-client <server:port>")
        return
    }
    
    mode := os.Args[1]
    
    switch mode {
    case "http":
        startHTTPServer()
    case "tcp-server":
        startTCPServer()
    case "tcp-client":
        if len(os.Args) < 3 {
            fmt.Println("请指定服务器地址")
            return
        }
        serverAddr := os.Args[2]
        startTCPClient(serverAddr)
    default:
        fmt.Println("无效模式")
    }
}

3.3 数据库操作

package main

import (
    "database/sql"
    "fmt"
    "log"
    "time"
    
    _ "github.com/go-sql-driver/mysql"
    _ "github.com/lib/pq"
    _ "github.com/mattn/go-sqlite3"
)

// 用户模型
type User struct {
    ID        int       `json:"id"`
    Name      string    `json:"name"`
    Email     string    `json:"email"`
    Age       int       `json:"age"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

// 数据库配置
type DBConfig struct {
    Driver   string
    Host     string
    Port     int
    Username string
    Password string
    Database string
}

// MySQL连接
func connectMySQL(config DBConfig) (*sql.DB, error) {
    dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true",
        config.Username, config.Password, config.Host, config.Port, config.Database)
    
    db, err := sql.Open("mysql", dsn)
    if err != nil {
        return nil, err
    }
    
    // 测试连接
    if err := db.Ping(); err != nil {
        return nil, err
    }
    
    // 设置连接池
    db.SetMaxOpenConns(25)
    db.SetMaxIdleConns(5)
    db.SetConnMaxLifetime(5 * time.Minute)
    
    return db, nil
}

// PostgreSQL连接
func connectPostgreSQL(config DBConfig) (*sql.DB, error) {
    dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
        config.Host, config.Port, config.Username, config.Password, config.Database)
    
    db, err := sql.Open("postgres", dsn)
    if err != nil {
        return nil, err
    }
    
    // 测试连接
    if err := db.Ping(); err != nil {
        return nil, err
    }
    
    // 设置连接池
    db.SetMaxOpenConns(25)
    db.SetMaxIdleConns(5)
    db.SetConnMaxLifetime(5 * time.Minute)
    
    return db, nil
}

// SQLite连接
func connectSQLite(dbPath string) (*sql.DB, error) {
    db, err := sql.Open("sqlite3", dbPath)
    if err != nil {
        return nil, err
    }
    
    // 测试连接
    if err := db.Ping(); err != nil {
        return nil, err
    }
    
    return db, nil
}

// 创建用户表
func createUserTable(db *sql.DB, driver string) error {
    var query string
    
    switch driver {
    case "mysql":
        query = `
        CREATE TABLE IF NOT EXISTS users (
            id INT AUTO_INCREMENT PRIMARY KEY,
            name VARCHAR(100) NOT NULL,
            email VARCHAR(100) UNIQUE NOT NULL,
            age INT,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
        )`
    case "postgres":
        query = `
        CREATE TABLE IF NOT EXISTS users (
            id SERIAL PRIMARY KEY,
            name VARCHAR(100) NOT NULL,
            email VARCHAR(100) UNIQUE NOT NULL,
            age INT,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )`
    case "sqlite3":
        query = `
        CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            email TEXT UNIQUE NOT NULL,
            age INTEGER,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
        )`
    default:
        return fmt.Errorf("不支持的数据库驱动: %s", driver)
    }
    
    _, err := db.Exec(query)
    return err
}

// 创建用户
func createUser(db *sql.DB, user User) (int, error) {
    query := "INSERT INTO users (name, email, age) VALUES (?, ?, ?)"
    
    result, err := db.Exec(query, user.Name, user.Email, user.Age)
    if err != nil {
        return 0, err
    }
    
    id, err := result.LastInsertId()
    if err != nil {
        return 0, err
    }
    
    return int(id), nil
}

// 根据ID获取用户
func getUserByID(db *sql.DB, id int) (User, error) {
    var user User
    query := "SELECT id, name, email, age, created_at, updated_at FROM users WHERE id = ?"
    
    err := db.QueryRow(query, id).Scan(
        &user.ID, &user.Name, &user.Email, &user.Age, &user.CreatedAt, &user.UpdatedAt)
    
    return user, err
}

// 获取所有用户
func getAllUsers(db *sql.DB) ([]User, error) {
    query := "SELECT id, name, email, age, created_at, updated_at FROM users ORDER BY id"
    
    rows, err := db.Query(query)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    
    var users []User
    for rows.Next() {
        var user User
        err := rows.Scan(
            &user.ID, &user.Name, &user.Email, &user.Age, &user.CreatedAt, &user.UpdatedAt)
        if err != nil {
            return nil, err
        }
        users = append(users, user)
    }
    
    if err = rows.Err(); err != nil {
        return nil, err
    }
    
    return users, nil
}

// 更新用户
func updateUser(db *sql.DB, user User) error {
    query := "UPDATE users SET name = ?, email = ?, age = ?, updated_at = ? WHERE id = ?"
    
    _, err := db.Exec(query, user.Name, user.Email, user.Age, time.Now(), user.ID)
    return err
}

// 删除用户
func deleteUser(db *sql.DB, id int) error {
    query := "DELETE FROM users WHERE id = ?"
    
    _, err := db.Exec(query, id)
    return err
}

// 事务示例
func transferUsers(db *sql.DB, fromID, toID int) error {
    tx, err := db.Begin()
    if err != nil {
        return err
    }
    
    defer func() {
        if p := recover(); p != nil {
            tx.Rollback()
            panic(p) // 重新抛出panic
        } else if err != nil {
            tx.Rollback()
        } else {
            err = tx.Commit()
        }
    }()
    
    // 获取源用户
    fromUser, err := getUserByID(db, fromID)
    if err != nil {
        return err
    }
    
    // 获取目标用户
    toUser, err := getUserByID(db, toID)
    if err != nil {
        return err
    }
    
    // 交换年龄
    fromUser.Age, toUser.Age = toUser.Age, fromUser.Age
    
    // 更新用户
    if err := updateUser(db, fromUser); err != nil {
        return err
    }
    
    if err := updateUser(db, toUser); err != nil {
        return err
    }
    
    return nil
}

func main() {
    // 使用SQLite作为示例(无需额外安装)
    db, err := connectSQLite("./test.db")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
    
    // 创建表
    if err := createUserTable(db, "sqlite3"); err != nil {
        log.Fatal(err)
    }
    
    // 创建用户
    users := []User{
        {Name: "张三", Email: "[email protected]", Age: 25},
        {Name: "李四", Email: "[email protected]", Age: 30},
        {Name: "王五", Email: "[email protected]", Age: 28},
    }
    
    for _, user := range users {
        id, err := createUser(db, user)
        if err != nil {
            log.Printf("创建用户失败: %v", err)
            continue
        }
        fmt.Printf("创建用户成功,ID: %d\n", id)
    }
    
    // 获取所有用户
    allUsers, err := getAllUsers(db)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println("\n所有用户:")
    for _, user := range allUsers {
        fmt.Printf("ID: %d, 姓名: %s, 邮箱: %s, 年龄: %d\n",
            user.ID, user.Name, user.Email, user.Age)
    }
    
    // 更新用户
    if len(allUsers) > 0 {
        user := allUsers[0]
        user.Age = 35
        if err := updateUser(db, user); err != nil {
            log.Printf("更新用户失败: %v", err)
        } else {
            fmt.Printf("\n用户 %s 年龄已更新为 %d\n", user.Name, user.Age)
        }
    }
    
    // 事务示例
    if len(allUsers) >= 2 {
        fromID := allUsers[0].ID
        toID := allUsers[1].ID
        
        fmt.Printf("\n交换用户 %d 和 %d 的年龄\n", fromID, toID)
        if err := transferUsers(db, fromID, toID); err != nil {
            log.Printf("事务失败: %v", err)
        } else {
            fmt.Println("事务成功")
        }
    }
    
    // 再次获取所有用户
    allUsers, err = getAllUsers(db)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println("\n更新后的用户:")
    for _, user := range allUsers {
        fmt.Printf("ID: %d, 姓名: %s, 邮箱: %s, 年龄: %d\n",
            user.ID, user.Name, user.Email, user.Age)
    }
    
    // 删除用户
    if len(allUsers) > 0 {
        user := allUsers[0]
        if err := deleteUser(db, user.ID); err != nil {
            log.Printf("删除用户失败: %v", err)
        } else {
            fmt.Printf("\n用户 %s 已删除\n", user.Name)
        }
    }
}

3.4 JSON/XML数据处理

package main

import (
    "encoding/json"
    "encoding/xml"
    "fmt"
    "io"
    "os"
    "reflect"
    "time"
)

// JSON序列化与反序列化
type Config struct {
    Host     string `json:"host"`
    Port     int    `json:"port"`
    Debug    bool   `json:"debug"`
    Database struct {
        Name     string `json:"name"`
        User     string `json:"user"`
        Password string `json:"password"`
    } `json:"database"`
}

// 用户结构体
type User struct {
    ID        int       `json:"id" xml:"id"`
    Name      string    `json:"name" xml:"name"`
    Email     string    `json:"email" xml:"email"`
    Age       int       `json:"age" xml:"age"`
    CreatedAt time.Time `json:"created_at" xml:"created_at"`
}

// 用户列表结构体(用于XML)
type UserList struct {
    XMLName xml.Name `xml:"users"`
    Users   []User   `xml:"user"`
}

// 保存配置为JSON
func saveConfig(config Config, filename string) error {
    data, err := json.MarshalIndent(config, "", "  ")
    if err != nil {
        return err
    }
    return os.WriteFile(filename, data, 0644)
}

// 从JSON加载配置
func loadConfig(filename string) (Config, error) {
    data, err := os.ReadFile(filename)
    if err != nil {
        return Config{}, err
    }
    
    var config Config
    err = json.Unmarshal(data, &config)
    return config, err
}

// 保存用户为JSON
func saveUsersToJSON(users []User, filename string) error {
    data, err := json.MarshalIndent(users, "", "  ")
    if err != nil {
        return err
    }
    return os.WriteFile(filename, data, 0644)
}

// 从JSON加载用户
func loadUsersFromJSON(filename string) ([]User, error) {
    data, err := os.ReadFile(filename)
    if err != nil {
        return nil, err
    }
    
    var users []User
    err = json.Unmarshal(data, &users)
    return users, err
}

// 保存用户为XML
func saveUsersToXML(users []User, filename string) error {
    userList := UserList{Users: users}
    data, err := xml.MarshalIndent(userList, "", "  ")
    if err != nil {
        return err
    }
    return os.WriteFile(filename, data, 0644)
}

// 从XML加载用户
func loadUsersFromXML(filename string) ([]User, error) {
    data, err := os.ReadFile(filename)
    if err != nil {
        return nil, err
    }
    
    var userList UserList
    err = xml.Unmarshal(data, &userList)
    return userList.Users, err
}

// 流式JSON处理
func streamJSONFile(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer file.Close()
    
    decoder := json.NewDecoder(file)
    
    // 读取开头的 [
    token, err := decoder.Token()
    if err != nil {
        return err
    }
    
    if token != json.Delim('[') {
        return fmt.Errorf("期望JSON数组开始,得到: %v", token)
    }
    
    // 读取数组中的每个对象
    for decoder.More() {
        var user User
        err := decoder.Decode(&user)
        if err != nil {
            return err
        }
        
        fmt.Printf("用户: %+v\n", user)
    }
    
    // 读取结尾的 ]
    token, err = decoder.Token()
    if err != nil {
        return err
    }
    
    if token != json.Delim(']') {
        return fmt.Errorf("期望JSON数组结束,得到: %v", token)
    }
    
    return nil
}

// 流式XML处理
func streamXMLFile(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer file.Close()
    
    decoder := xml.NewDecoder(file)
    
    for {
        token, err := decoder.Token()
        if err == io.EOF {
            break
        }
        if err != nil {
            return err
        }
        
        switch se := token.(type) {
        case xml.StartElement:
            if se.Name.Local == "user" {
                var user User
                err := decoder.DecodeElement(&user, &se)
                if err != nil {
                    return err
                }
                fmt.Printf("用户: %+v\n", user)
            }
        }
    }
    
    return nil
}

// 反射获取结构体标签
func printStructTags(obj interface{}) {
    v := reflect.ValueOf(obj)
    t := reflect.TypeOf(obj)
    
    if t.Kind() == reflect.Ptr {
        t = t.Elem()
        v = v.Elem()
    }
    
    if t.Kind() != reflect.Struct {
        fmt.Println("不是结构体")
        return
    }
    
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        jsonTag := field.Tag.Get("json")
        xmlTag := field.Tag.Get("xml")
        
        fmt.Printf("字段: %s, 类型: %v, JSON标签: %s, XML标签: %s\n",
            field.Name, field.Type, jsonTag, xmlTag)
    }
}

func main() {
    // 创建示例配置
    config := Config{
        Host:  "localhost",
        Port:  8080,
        Debug: true,
    }
    config.Database.Name = "mydb"
    config.Database.User = "admin"
    config.Database.Password = "password"
    
    // 保存配置
    err := saveConfig(config, "config.json")
    if err != nil {
        fmt.Printf("保存配置失败: %v\n", err)
        return
    }
    
    // 加载配置
    loadedConfig, err := loadConfig("config.json")
    if err != nil {
        fmt.Printf("加载配置失败: %v\n", err)
        return
    }
    
    fmt.Printf("加载的配置: %+v\n", loadedConfig)
    
    // 创建示例用户
    users := []User{
        {ID: 1, Name: "张三", Email: "[email protected]", Age: 25, CreatedAt: time.Now()},
        {ID: 2, Name: "李四", Email: "[email protected]", Age: 30, CreatedAt: time.Now()},
        {ID: 3, Name: "王五", Email: "[email protected]", Age: 28, CreatedAt: time.Now()},
    }
    
    // 保存用户为JSON
    err = saveUsersToJSON(users, "users.json")
    if err != nil {
        fmt.Printf("保存用户JSON失败: %v\n", err)
        return
    }
    
    // 保存用户为XML
    err = saveUsersToXML(users, "users.xml")
    if err != nil {
        fmt.Printf("保存用户XML失败: %v\n", err)
        return
    }
    
    // 从JSON加载用户
    loadedUsers, err := loadUsersFromJSON("users.json")
    if err != nil {
        fmt.Printf("加载用户JSON失败: %v\n", err)
        return
    }
    
    fmt.Println("\n从JSON加载的用户:")
    for _, user := range loadedUsers {
        fmt.Printf("ID: %d, 姓名: %s, 邮箱: %s, 年龄: %d\n",
            user.ID, user.Name, user.Email, user.Age)
    }
    
    // 从XML加载用户
    loadedUsersFromXML, err := loadUsersFromXML("users.xml")
    if err != nil {
        fmt.Printf("加载用户XML失败: %v\n", err)
        return
    }
    
    fmt.Println("\n从XML加载的用户:")
    for _, user := range loadedUsersFromXML {
        fmt.Printf("ID: %d, 姓名: %s, 邮箱: %s, 年龄: %d\n",
            user.ID, user.Name, user.Email, user.Age)
    }
    
    // 流式处理JSON
    fmt.Println("\n流式处理JSON:")
    err = streamJSONFile("users.json")
    if err != nil {
        fmt.Printf("流式处理JSON失败: %v\n", err)
    }
    
    // 流式处理XML
    fmt.Println("\n流式处理XML:")
    err = streamXMLFile("users.xml")
    if err != nil {
        fmt.Printf("流式处理XML失败: %v\n", err)
    }
    
    // 打印结构体标签
    fmt.Println("\nUser结构体标签:")
    printStructTags(User{})
    
    // 清理文件
    os.Remove("config.json")
    os.Remove("users.json")
    os.Remove("users.xml")
}

3.5 测试与调试

package main

import (
    "fmt"
    "math"
    "os"
    "runtime"
    "runtime/pprof"
    "sort"
    "testing"
    "time"
)

// 被测试的函数
func add(a, b int) int {
    return a + b
}

func multiply(a, b int) int {
    return a * b
}

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("division by zero")
    }
    return a / b, nil
}

// 计算斐波那契数列
func fibonacci(n int) int {
    if n <= 1 {
        return n
    }
    return fibonacci(n-1) + fibonacci(n-2)
}

// 优化的斐波那契数列
func fibonacciOptimized(n int) int {
    if n <= 1 {
        return n
    }
    
    a, b := 0, 1
    for i := 2; i <= n; i++ {
        a, b = b, a+b
    }
    return b
}

// 排序函数
func bubbleSort(arr []int) []int {
    n := len(arr)
    result := make([]int, n)
    copy(result, arr)
    
    for i := 0; i < n-1; i++ {
        for j := 0; j < n-i-1; j++ {
            if result[j] > result[j+1] {
                result[j], result[j+1] = result[j+1], result[j]
            }
        }
    }
    
    return result
}

// 单元测试
func TestAdd(t *testing.T) {
    tests := []struct {
        a, b     int
        expected int
    }{
        {1, 2, 3},
        {-1, 1, 0},
        {0, 0, 0},
        {100, 200, 300},
    }
    
    for _, test := range tests {
        result := add(test.a, test.b)
        if result != test.expected {
            t.Errorf("add(%d, %d) = %d; expected %d", test.a, test.b, result, test.expected)
        }
    }
}

func TestMultiply(t *testing.T) {
    tests := []struct {
        a, b     int
        expected int
    }{
        {2, 3, 6},
        {-2, 3, -6},
        {0, 5, 0},
        {10, 10, 100},
    }
    
    for _, test := range tests {
        result := multiply(test.a, test.b)
        if result != test.expected {
            t.Errorf("multiply(%d, %d) = %d; expected %d", test.a, test.b, result, test.expected)
        }
    }
}

func TestDivide(t *testing.T) {
    // 测试正常情况
    result, err := divide(10, 2)
    if err != nil {
        t.Errorf("divide(10, 2) returned error: %v", err)
    }
    if math.Abs(result-5.0) > 1e-9 {
        t.Errorf("divide(10, 2) = %f; expected 5.0", result)
    }
    
    // 测试除零错误
    _, err = divide(10, 0)
    if err == nil {
        t.Error("divide(10, 0) should return an error")
    }
}

func TestFibonacci(t *testing.T) {
    tests := []struct {
        n, expected int
    }{
        {0, 0},
        {1, 1},
        {2, 1},
        {3, 2},
        {4, 3},
        {5, 5},
        {10, 55},
    }
    
    for _, test := range tests {
        result := fibonacci(test.n)
        if result != test.expected {
            t.Errorf("fibonacci(%d) = %d; expected %d", test.n, result, test.expected)
        }
    }
}

func TestFibonacciOptimized(t *testing.T) {
    tests := []struct {
        n, expected int
    }{
        {0, 0},
        {1, 1},
        {2, 1},
        {3, 2},
        {4, 3},
        {5, 5},
        {10, 55},
        {20, 6765},
    }
    
    for _, test := range tests {
        result := fibonacciOptimized(test.n)
        if result != test.expected {
            t.Errorf("fibonacciOptimized(%d) = %d; expected %d", test.n, result, test.expected)
        }
    }
}

func TestBubbleSort(t *testing.T) {
    tests := []struct {
        input    []int
        expected []int
    }{
        {[]int{5, 3, 8, 4, 2}, []int{2, 3, 4, 5, 8}},
        {[]int{1, 2, 3, 4, 5}, []int{1, 2, 3, 4, 5}},
        {[]int{5, 4, 3, 2, 1}, []int{1, 2, 3, 4, 5}},
        {[]int{}, []int{}},
        {[]int{42}, []int{42}},
    }
    
    for _, test := range tests {
        result := bubbleSort(test.input)
        if !equal(result, test.expected) {
            t.Errorf("bubbleSort(%v) = %v; expected %v", test.input, result, test.expected)
        }
    }
}

// 辅助函数:比较两个整数切片是否相等
func equal(a, b []int) bool {
    if len(a) != len(b) {
        return false
    }
    for i := range a {
        if a[i] != b[i] {
            return false
        }
    }
    return true
}

// 基准测试
func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        add(1, 2)
    }
}

func BenchmarkMultiply(b *testing.B) {
    for i := 0; i < b.N; i++ {
        multiply(123, 456)
    }
}

func BenchmarkFibonacci(b *testing.B) {
    for i := 0; i < b.N; i++ {
        fibonacci(10)
    }
}

func BenchmarkFibonacciOptimized(b *testing.B) {
    for i := 0; i < b.N; i++ {
        fibonacciOptimized(10)
    }
}

func BenchmarkBubbleSort(b *testing.B) {
    // 创建测试数据
    data := make([]int, 100)
    for i := range data {
        data[i] = 100 - i // 逆序数组
    }
    
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        bubbleSort(data)
    }
}

func BenchmarkSort(b *testing.B) {
    // 创建测试数据
    data := make([]int, 100)
    for i := range data {
        data[i] = 100 - i // 逆序数组
    }
    
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        // 复制数据以避免修改原始数据
        testData := make([]int, len(data))
        copy(testData, data)
        sort.Ints(testData)
    }
}

// 性能分析
func profileCPU(filename string, fn func()) error {
    f, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer f.Close()
    
    if err := pprof.StartCPUProfile(f); err != nil {
        return err
    }
    defer pprof.StopCPUProfile()
    
    fn()
    return nil
}

// 内存分析
func profileMemory(filename string, fn func()) error {
    f, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer f.Close()
    
    runtime.GC() // 获取最新的GC数据
    
    if err := pprof.WriteHeapProfile(f); err != nil {
        return err
    }
    
    fn()
    return nil
}

// 示例函数
func ExampleAdd() {
    result := add(2, 3)
    fmt.Println(result)
    // Output: 5
}

func ExampleFibonacci() {
    fmt.Println(fibonacci(5))
    // Output: 5
}

// 测试主函数
func main() {
    // 性能分析示例
    fmt.Println("开始性能分析...")
    
    // CPU性能分析
    err := profileCPU("cpu.prof", func() {
        // 执行一些计算密集型任务
        for i := 0; i < 1000; i++ {
            fibonacciOptimized(20)
        }
    })
    
    if err != nil {
        fmt.Printf("CPU性能分析失败: %v\n", err)
    } else {
        fmt.Println("CPU性能分析完成,结果保存到 cpu.prof")
    }
    
    // 内存性能分析
    err = profileMemory("mem.prof", func() {
        // 创建一些数据
        data := make([][]int, 100)
        for i := range data {
            data[i] = make([]int, 1000)
            for j := range data[i] {
                data[i][j] = i * j
            }
        }
    })
    
    if err != nil {
        fmt.Printf("内存性能分析失败: %v\n", err)
    } else {
        fmt.Println("内存性能分析完成,结果保存到 mem.prof")
    }
    
    // 运行基准测试
    fmt.Println("\n运行基准测试...")
    result := testing.Benchmark(BenchmarkFibonacci)
    fmt.Printf("fibonacci(10) 基准测试结果: %s\n", result)
    
    result = testing.Benchmark(BenchmarkFibonacciOptimized)
    fmt.Printf("fibonacciOptimized(10) 基准测试结果: %s\n", result)
    
    // 比较两种斐波那契实现的性能
    n := 30
    start := time.Now()
    result1 := fibonacci(n)
    duration1 := time.Since(start)
    
    start = time.Now()
    result2 := fibonacciOptimized(n)
    duration2 := time.Since(start)
    
    fmt.Printf("\n斐波那契数列第%d项:\n", n)
    fmt.Printf("递归实现: %d, 耗时: %v\n", result1, duration1)
    fmt.Printf("优化实现: %d, 耗时: %v\n", result2, duration2)
    fmt.Printf("性能提升: %.2fx\n", float64(duration1)/float64(duration2))
    
    // 清理分析文件
    os.Remove("cpu.prof")
    os.Remove("mem.prof")
}

练习项目

项目1:文件备份工具

package main

import (
    "archive/zip"
    "bufio"
    "crypto/md5"
    "fmt"
    "io"
    "os"
    "path/filepath"
    "sort"
    "strings"
    "time"
)

// 文件信息
type FileInfo struct {
    Path     string
    Size     int64
    ModTime  time.Time
    Hash     string
    IsDir    bool
}

// 备份信息
type BackupInfo struct {
    Name      string
    Timestamp time.Time
    Files     []FileInfo
    Size      int64
}

// 备份管理器
type BackupManager struct {
    sourceDir string
    backupDir string
    indexFile string
}

// 创建新的备份管理器
func NewBackupManager(sourceDir, backupDir string) *BackupManager {
    return &BackupManager{
        sourceDir: sourceDir,
        backupDir: backupDir,
        indexFile: filepath.Join(backupDir, "index.json"),
    }
}

// 获取文件哈希
func getFileHash(filePath string) (string, error) {
    file, err := os.Open(filePath)
    if err != nil {
        return "", err
    }
    defer file.Close()
    
    hash := md5.New()
    if _, err := io.Copy(hash, file); err != nil {
        return "", err
    }
    
    return fmt.Sprintf("%x", hash.Sum(nil)), nil
}

// 扫描目录获取文件信息
func (bm *BackupManager) scanDirectory() ([]FileInfo, error) {
    var files []FileInfo
    
    err := filepath.Walk(bm.sourceDir, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        
        // 获取相对路径
        relPath, err := filepath.Rel(bm.sourceDir, path)
        if err != nil {
            return err
        }
        
        // 跳过备份目录本身
        if strings.HasPrefix(relPath, bm.backupDir) {
            return nil
        }
        
        fileHash := ""
        if !info.IsDir() {
            fileHash, err = getFileHash(path)
            if err != nil {
                return err
            }
        }
        
        files = append(files, FileInfo{
            Path:    relPath,
            Size:    info.Size(),
            ModTime: info.ModTime(),
            Hash:    fileHash,
            IsDir:   info.IsDir(),
        })
        
        return nil
    })
    
    return files, err
}

// 创建备份
func (bm *BackupManager) Backup() error {
    // 确保备份目录存在
    if err := os.MkdirAll(bm.backupDir, 0755); err != nil {
        return fmt.Errorf("创建备份目录失败: %v", err)
    }
    
    // 扫描源目录
    files, err := bm.scanDirectory()
    if err != nil {
        return fmt.Errorf("扫描目录失败: %v", err)
    }
    
    // 创建备份名称
    timestamp := time.Now().Format("20060102_150405")
    backupName := fmt.Sprintf("backup_%s.zip", timestamp)
    backupPath := filepath.Join(bm.backupDir, backupName)
    
    // 创建ZIP文件
    zipFile, err := os.Create(backupPath)
    if err != nil {
        return fmt.Errorf("创建备份文件失败: %v", err)
    }
    defer zipFile.Close()
    
    zipWriter := zip.NewWriter(zipFile)
    defer zipWriter.Close()
    
    var totalSize int64
    
    // 添加文件到ZIP
    for _, file := range files {
        if file.IsDir {
            continue
        }
        
        // 打开源文件
        sourcePath := filepath.Join(bm.sourceDir, file.Path)
        sourceFile, err := os.Open(sourcePath)
        if err != nil {
            return fmt.Errorf("打开源文件失败: %v", err)
        }
        
        // 在ZIP中创建文件
        header, err := zip.FileInfoHeader(fileInfoFromPath(sourcePath))
        if err != nil {
            sourceFile.Close()
            return fmt.Errorf("创建ZIP头失败: %v", err)
        }
        header.Name = file.Path
        header.Method = zip.Deflate
        
        writer, err := zipWriter.CreateHeader(header)
        if err != nil {
            sourceFile.Close()
            return fmt.Errorf("在ZIP中创建文件失败: %v", err)
        }
        
        // 复制文件内容
        _, err = io.Copy(writer, sourceFile)
        sourceFile.Close()
        
        if err != nil {
            return fmt.Errorf("复制文件内容失败: %v", err)
        }
        
        totalSize += file.Size
    }
    
    // 保存备份信息
    backupInfo := BackupInfo{
        Name:      backupName,
        Timestamp: time.Now(),
        Files:     files,
        Size:      totalSize,
    }
    
    if err := bm.saveBackupInfo(backupInfo); err != nil {
        return fmt.Errorf("保存备份信息失败: %v", err)
    }
    
    fmt.Printf("备份完成: %s (大小: %.2f MB)\n", backupName, float64(totalSize)/1024/1024)
    return nil
}

// 从路径创建文件信息
func fileInfoFromPath(path string) os.FileInfo {
    info, _ := os.Stat(path)
    return info
}

// 保存备份信息
func (bm *BackupManager) saveBackupInfo(info BackupInfo) error {
    // 这里简化处理,实际应用中可以使用JSON或其他格式
    indexFile, err := os.OpenFile(bm.indexFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        return err
    }
    defer indexFile.Close()
    
    writer := bufio.NewWriter(indexFile)
    defer writer.Flush()
    
    line := fmt.Sprintf("%s|%s|%d\n", info.Name, info.Timestamp.Format(time.RFC3339), info.Size)
    _, err = writer.WriteString(line)
    return err
}

// 列出所有备份
func (bm *BackupManager) ListBackups() ([]BackupInfo, error) {
    file, err := os.Open(bm.indexFile)
    if err != nil {
        if os.IsNotExist(err) {
            return []BackupInfo{}, nil
        }
        return nil, err
    }
    defer file.Close()
    
    var backups []BackupInfo
    scanner := bufio.NewScanner(file)
    
    for scanner.Scan() {
        line := scanner.Text()
        parts := strings.Split(line, "|")
        if len(parts) != 3 {
            continue
        }
        
        timestamp, err := time.Parse(time.RFC3339, parts[1])
        if err != nil {
            continue
        }
        
        var size int64
        fmt.Sscanf(parts[2], "%d", &size)
        
        backups = append(backups, BackupInfo{
            Name:      parts[0],
            Timestamp: timestamp,
            Size:      size,
        })
    }
    
    // 按时间戳排序
    sort.Slice(backups, func(i, j int) bool {
        return backups[i].Timestamp.After(backups[j].Timestamp)
    })
    
    return backups, nil
}

// 恢复备份
func (bm *BackupManager) Restore(backupName string) error {
    backupPath := filepath.Join(bm.backupDir, backupName)
    
    // 打开ZIP文件
    reader, err := zip.OpenReader(backupPath)
    if err != nil {
        return fmt.Errorf("打开备份文件失败: %v", err)
    }
    defer reader.Close()
    
    // 解压文件
    for _, file := range reader.File {
        path := filepath.Join(bm.sourceDir, file.Name)
        
        // 创建目录
        if file.FileInfo().IsDir() {
            if err := os.MkdirAll(path, file.FileInfo().Mode()); err != nil {
                return fmt.Errorf("创建目录失败: %v", err)
            }
            continue
        }
        
        // 确保父目录存在
        if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
            return fmt.Errorf("创建父目录失败: %v", err)
        }
        
        // 打开ZIP中的文件
        fileReader, err := file.Open()
        if err != nil {
            return fmt.Errorf("打开ZIP中的文件失败: %v", err)
        }
        
        // 创建目标文件
        targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.FileInfo().Mode())
        if err != nil {
            fileReader.Close()
            return fmt.Errorf("创建目标文件失败: %v", err)
        }
        
        // 复制文件内容
        _, err = io.Copy(targetFile, fileReader)
        fileReader.Close()
        targetFile.Close()
        
        if err != nil {
            return fmt.Errorf("复制文件内容失败: %v", err)
        }
    }
    
    fmt.Printf("恢复完成: %s\n", backupName)
    return nil
}

// 增量备份
func (bm *BackupManager) IncrementalBackup() error {
    // 获取最新的备份信息
    backups, err := bm.ListBackups()
    if err != nil {
        return err
    }
    
    if len(backups) == 0 {
        // 如果没有备份,执行完整备份
        return bm.Backup()
    }
    
    // 获取当前文件信息
    currentFiles, err := bm.scanDirectory()
    if err != nil {
        return err
    }
    
    // 这里简化处理,实际应用中需要比较文件哈希和修改时间
    // 只备份有变化的文件
    var changedFiles []FileInfo
    for _, file := range currentFiles {
        if file.IsDir {
            continue
        }
        
        // 简化处理:备份最近修改的文件
        if time.Since(file.ModTime) < 24*time.Hour {
            changedFiles = append(changedFiles, file)
        }
    }
    
    if len(changedFiles) == 0 {
        fmt.Println("没有文件需要备份")
        return nil
    }
    
    // 创建增量备份
    timestamp := time.Now().Format("20060102_150405")
    backupName := fmt.Sprintf("incremental_%s.zip", timestamp)
    backupPath := filepath.Join(bm.backupDir, backupName)
    
    // 创建ZIP文件
    zipFile, err := os.Create(backupPath)
    if err != nil {
        return fmt.Errorf("创建备份文件失败: %v", err)
    }
    defer zipFile.Close()
    
    zipWriter := zip.NewWriter(zipFile)
    defer zipWriter.Close()
    
    var totalSize int64
    
    // 添加变化的文件到ZIP
    for _, file := range changedFiles {
        sourcePath := filepath.Join(bm.sourceDir, file.Path)
        sourceFile, err := os.Open(sourcePath)
        if err != nil {
            return fmt.Errorf("打开源文件失败: %v", err)
        }
        
        header, err := zip.FileInfoHeader(fileInfoFromPath(sourcePath))
        if err != nil {
            sourceFile.Close()
            return fmt.Errorf("创建ZIP头失败: %v", err)
        }
        header.Name = file.Path
        header.Method = zip.Deflate
        
        writer, err := zipWriter.CreateHeader(header)
        if err != nil {
            sourceFile.Close()
            return fmt.Errorf("在ZIP中创建文件失败: %v", err)
        }
        
        _, err = io.Copy(writer, sourceFile)
        sourceFile.Close()
        
        if err != nil {
            return fmt.Errorf("复制文件内容失败: %v", err)
        }
        
        totalSize += file.Size
    }
    
    // 保存备份信息
    backupInfo := BackupInfo{
        Name:      backupName,
        Timestamp: time.Now(),
        Files:     changedFiles,
        Size:      totalSize,
    }
    
    if err := bm.saveBackupInfo(backupInfo); err != nil {
        return fmt.Errorf("保存备份信息失败: %v", err)
    }
    
    fmt.Printf("增量备份完成: %s (文件数: %d, 大小: %.2f MB)\n", 
        backupName, len(changedFiles), float64(totalSize)/1024/1024)
    return nil
}

func main() {
    if len(os.Args) < 3 {
        fmt.Println("使用方法:")
        fmt.Println("  完整备份: go run backup.go backup <源目录> <备份目录>")
        fmt.Println("  增量备份: go run backup.go incremental <源目录> <备份目录>")
        fmt.Println("  恢复备份: go run backup.go restore <源目录> <备份目录> <备份文件名>")
        fmt.Println("  列出备份: go run backup.go list <备份目录>")
        return
    }
    
    command := os.Args[1]
    
    switch command {
    case "backup":
        if len(os.Args) < 4 {
            fmt.Println("请指定源目录和备份目录")
            return
        }
        sourceDir := os.Args[2]
        backupDir := os.Args[3]
        
        manager := NewBackupManager(sourceDir, backupDir)
        if err := manager.Backup(); err != nil {
            fmt.Printf("备份失败: %v\n", err)
        }
        
    case "incremental":
        if len(os.Args) < 4 {
            fmt.Println("请指定源目录和备份目录")
            return
        }
        sourceDir := os.Args[2]
        backupDir := os.Args[3]
        
        manager := NewBackupManager(sourceDir, backupDir)
        if err := manager.IncrementalBackup(); err != nil {
            fmt.Printf("增量备份失败: %v\n", err)
        }
        
    case "restore":
        if len(os.Args) < 5 {
            fmt.Println("请指定源目录、备份目录和备份文件名")
            return
        }
        sourceDir := os.Args[2]
        backupDir := os.Args[3]
        backupName := os.Args[4]
        
        manager := NewBackupManager(sourceDir, backupDir)
        if err := manager.Restore(backupName); err != nil {
            fmt.Printf("恢复失败: %v\n", err)
        }
        
    case "list":
        if len(os.Args) < 3 {
            fmt.Println("请指定备份目录")
            return
        }
        backupDir := os.Args[2]
        
        manager := NewBackupManager("", backupDir)
        backups, err := manager.ListBackups()
        if err != nil {
            fmt.Printf("列出备份失败: %v\n", err)
            return
        }
        
        if len(backups) == 0 {
            fmt.Println("没有找到备份")
            return
        }
        
        fmt.Println("可用备份:")
        for _, backup := range backups {
            fmt.Printf("%s - %s (%.2f MB)\n", 
                backup.Name, 
                backup.Timestamp.Format("2006-01-02 15:04:05"), 
                float64(backup.Size)/1024/1024)
        }
        
    default:
        fmt.Println("未知命令")
    }
}

项目2:Web爬虫

package main

import (
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "regexp"
    "strings"
    "sync"
    "time"
)

// 网页信息
type PageInfo struct {
    URL   string
    Title string
    Links []string
    Error error
}

// 爬虫结构体
type Crawler struct {
    visited    map[string]bool
    mutex      sync.Mutex
    maxDepth   int
    delay      time.Duration
    userAgent  string
    resultChan chan PageInfo
    wg         sync.WaitGroup
}

// 创建新的爬虫
func NewCrawler(maxDepth int, delay time.Duration) *Crawler {
    return &Crawler{
        visited:    make(map[string]bool),
        maxDepth:   maxDepth,
        delay:      delay,
        userAgent:  "GoCrawler/1.0",
        resultChan: make(chan PageInfo, 100),
    }
}

// 提取网页标题
func extractTitle(html string) string {
    // 简单的正则表达式提取标题
    titleRegex := regexp.MustCompile(`<title>(.*?)</title>`)
    matches := titleRegex.FindStringSubmatch(html)
    if len(matches) > 1 {
        return strings.TrimSpace(matches[1])
    }
    return ""
}

// 提取链接
func (c *Crawler) extractLinks(baseURL string, html string) []string {
    // 提取所有href属性
    linkRegex := regexp.MustCompile(`<a[^>]+href=["']([^"']+)["']`)
    matches := linkRegex.FindAllStringSubmatch(html, -1)
    
    var links []string
    base, err := url.Parse(baseURL)
    if err != nil {
        return links
    }
    
    for _, match := range matches {
        if len(match) > 1 {
            href := match[1]
            
            // 解析相对URL
            parsedURL, err := url.Parse(href)
            if err != nil {
                continue
            }
            
            absoluteURL := base.ResolveReference(parsedURL).String()
            
            // 只保留同域名的链接
            if parsedURL.Host == "" || parsedURL.Host == base.Host {
                links = append(links, absoluteURL)
            }
        }
    }
    
    return links
}

// 获取网页内容
func (c *Crawler) fetchPage(urlStr string) (string, error) {
    // 检查是否已访问
    c.mutex.Lock()
    if c.visited[urlStr] {
        c.mutex.Unlock()
        return "", fmt.Errorf("URL已访问: %s", urlStr)
    }
    c.visited[urlStr] = true
    c.mutex.Unlock()
    
    // 创建请求
    req, err := http.NewRequest("GET", urlStr, nil)
    if err != nil {
        return "", err
    }
    
    req.Header.Set("User-Agent", c.userAgent)
    
    // 发送请求
    client := &http.Client{Timeout: 10 * time.Second}
    resp, err := client.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    
    // 检查状态码
    if resp.StatusCode != http.StatusOK {
        return "", fmt.Errorf("HTTP错误: %d", resp.StatusCode)
    }
    
    // 读取内容
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return "", err
    }
    
    // 检查内容类型
    contentType := resp.Header.Get("Content-Type")
    if !strings.Contains(contentType, "text/html") {
        return "", fmt.Errorf("非HTML内容: %s", contentType)
    }
    
    return string(body), nil
}

// 爬取单个页面
func (c *Crawler) crawlPage(urlStr string, depth int) {
    defer c.wg.Done()
    
    // 检查深度
    if depth > c.maxDepth {
        return
    }
    
    // 延迟请求
    time.Sleep(c.delay)
    
    // 获取页面内容
    html, err := c.fetchPage(urlStr)
    
    // 提取信息
    pageInfo := PageInfo{
        URL:   urlStr,
        Error: err,
    }
    
    if err == nil {
        pageInfo.Title = extractTitle(html)
        pageInfo.Links = c.extractLinks(urlStr, html)
        
        // 爬取链接页面
        for _, link := range pageInfo.Links {
            c.wg.Add(1)
            go c.crawlPage(link, depth+1)
        }
    }
    
    // 发送结果
    c.resultChan <- pageInfo
}

// 开始爬取
func (c *Crawler) Crawl(startURL string) []PageInfo {
    // 启动爬取
    c.wg.Add(1)
    go c.crawlPage(startURL, 0)
    
    // 启动结果收集器
    var results []PageInfo
    done := make(chan struct{})
    
    go func() {
        for page := range c.resultChan {
            results = append(results, page)
        }
        close(done)
    }()
    
    // 等待所有爬取完成
    c.wg.Wait()
    close(c.resultChan)
    
    // 等待结果收集完成
    <-done
    
    return results
}

// 保存结果到文件
func saveResults(results []PageInfo, filename string) error {
    file, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer file.Close()
    
    for _, page := range results {
        if page.Error != nil {
            fmt.Fprintf(file, "URL: %s\n错误: %v\n\n", page.URL, page.Error)
        } else {
            fmt.Fprintf(file, "URL: %s\n标题: %s\n链接数: %d\n\n", 
                page.URL, page.Title, len(page.Links))
        }
    }
    
    return nil
}

// 打印统计信息
func printStats(results []PageInfo) {
    var successCount, errorCount int
    var totalLinks int
    
    for _, page := range results {
        if page.Error != nil {
            errorCount++
        } else {
            successCount++
            totalLinks += len(page.Links)
        }
    }
    
    fmt.Printf("\n爬取统计:\n")
    fmt.Printf("成功页面: %d\n", successCount)
    fmt.Printf("失败页面: %d\n", errorCount)
    fmt.Printf("总链接数: %d\n", totalLinks)
    fmt.Printf("平均每页链接数: %.2f\n", float64(totalLinks)/float64(successCount))
}

func main() {
    if len(os.Args) < 2 {
        fmt.Println("使用方法: go run crawler.go <起始URL> [深度] [延迟(秒)]")
        return
    }
    
    startURL := os.Args[1]
    maxDepth := 2
    delay := 1 * time.Second
    
    if len(os.Args) >= 3 {
        fmt.Sscanf(os.Args[2], "%d", &maxDepth)
    }
    
    if len(os.Args) >= 4 {
        delaySec := 0
        fmt.Sscanf(os.Args[3], "%d", &delaySec)
        delay = time.Duration(delaySec) * time.Second
    }
    
    fmt.Printf("开始爬取: %s (深度: %d, 延迟: %v)\n", startURL, maxDepth, delay)
    
    // 创建爬虫
    crawler := NewCrawler(maxDepth, delay)
    
    // 开始爬取
    start := time.Now()
    results := crawler.Crawl(startURL)
    duration := time.Since(start)
    
    // 打印结果
    fmt.Printf("\n爬取完成,耗时: %v\n", duration)
    printStats(results)
    
    // 保存结果
    filename := fmt.Sprintf("crawl_results_%s.txt", time.Now().Format("20060102_150405"))
    if err := saveResults(results, filename); err != nil {
        fmt.Printf("保存结果失败: %v\n", err)
    } else {
        fmt.Printf("结果已保存到: %s\n", filename)
    }
    
    // 打印前几个结果
    fmt.Println("\n前5个结果:")
    count := 0
    for _, page := range results {
        if count >= 5 {
            break
        }
        
        if page.Error != nil {
            fmt.Printf("%d. %s - 错误: %v\n", count+1, page.URL, page.Error)
        } else {
            fmt.Printf("%d. %s - 标题: %s\n", count+1, page.URL, page.Title)
        }
        count++
    }
}

项目3:数据库CRUD应用

package main

import (
    "database/sql"
    "fmt"
    "log"
    "os"
    "strconv"
    "strings"
    "time"
    
    _ "github.com/mattn/go-sqlite3"
)

// 用户模型
type User struct {
    ID        int       `json:"id"`
    Name      string    `json:"name"`
    Email     string    `json:"email"`
    Age       int       `json:"age"`
    Address   string    `json:"address"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

// 用户服务
type UserService struct {
    db *sql.DB
}

// 创建新的用户服务
func NewUserService(dbPath string) (*UserService, error) {
    db, err := sql.Open("sqlite3", dbPath)
    if err != nil {
        return nil, err
    }
    
    // 测试连接
    if err := db.Ping(); err != nil {
        return nil, err
    }
    
    // 创建表
    if err := createUserTable(db); err != nil {
        return nil, err
    }
    
    return &UserService{db: db}, nil
}

// 创建用户表
func createUserTable(db *sql.DB) error {
    query := `
    CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        email TEXT UNIQUE NOT NULL,
        age INTEGER,
        address TEXT,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
        updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
    )`
    
    _, err := db.Exec(query)
    return err
}

// 创建用户
func (us *UserService) CreateUser(user User) (int, error) {
    query := `
    INSERT INTO users (name, email, age, address) 
    VALUES (?, ?, ?, ?)
    `
    
    result, err := us.db.Exec(query, user.Name, user.Email, user.Age, user.Address)
    if err != nil {
        return 0, fmt.Errorf("创建用户失败: %v", err)
    }
    
    id, err := result.LastInsertId()
    if err != nil {
        return 0, fmt.Errorf("获取用户ID失败: %v", err)
    }
    
    return int(id), nil
}

// 根据ID获取用户
func (us *UserService) GetUser(id int) (User, error) {
    query := `
    SELECT id, name, email, age, address, created_at, updated_at 
    FROM users 
    WHERE id = ?
    `
    
    var user User
    err := us.db.QueryRow(query, id).Scan(
        &user.ID, &user.Name, &user.Email, &user.Age, 
        &user.Address, &user.CreatedAt, &user.UpdatedAt)
    
    if err != nil {
        if err == sql.ErrNoRows {
            return User{}, fmt.Errorf("用户不存在")
        }
        return User{}, fmt.Errorf("获取用户失败: %v", err)
    }
    
    return user, nil
}

// 根据邮箱获取用户
func (us *UserService) GetUserByEmail(email string) (User, error) {
    query := `
    SELECT id, name, email, age, address, created_at, updated_at 
    FROM users 
    WHERE email = ?
    `
    
    var user User
    err := us.db.QueryRow(query, email).Scan(
        &user.ID, &user.Name, &user.Email, &user.Age, 
        &user.Address, &user.CreatedAt, &user.UpdatedAt)
    
    if err != nil {
        if err == sql.ErrNoRows {
            return User{}, fmt.Errorf("用户不存在")
        }
        return User{}, fmt.Errorf("获取用户失败: %v", err)
    }
    
    return user, nil
}

// 更新用户
func (us *UserService) UpdateUser(user User) error {
    query := `
    UPDATE users 
    SET name = ?, email = ?, age = ?, address = ?, updated_at = ? 
    WHERE id = ?
    `
    
    _, err := us.db.Exec(query, user.Name, user.Email, user.Age, user.Address, time.Now(), user.ID)
    if err != nil {
        return fmt.Errorf("更新用户失败: %v", err)
    }
    
    return nil
}

// 删除用户
func (us *UserService) DeleteUser(id int) error {
    query := `DELETE FROM users WHERE id = ?`
    
    result, err := us.db.Exec(query, id)
    if err != nil {
        return fmt.Errorf("删除用户失败: %v", err)
    }
    
    rowsAffected, err := result.RowsAffected()
    if err != nil {
        return fmt.Errorf("获取影响行数失败: %v", err)
    }
    
    if rowsAffected == 0 {
        return fmt.Errorf("用户不存在")
    }
    
    return nil
}

// 获取所有用户
func (us *UserService) ListUsers() ([]User, error) {
    query := `
    SELECT id, name, email, age, address, created_at, updated_at 
    FROM users 
    ORDER BY id
    `
    
    rows, err := us.db.Query(query)
    if err != nil {
        return nil, fmt.Errorf("查询用户失败: %v", err)
    }
    defer rows.Close()
    
    var users []User
    for rows.Next() {
        var user User
        err := rows.Scan(
            &user.ID, &user.Name, &user.Email, &user.Age, 
            &user.Address, &user.CreatedAt, &user.UpdatedAt)
        
        if err != nil {
            return nil, fmt.Errorf("扫描用户数据失败: %v", err)
        }
        
        users = append(users, user)
    }
    
    if err = rows.Err(); err != nil {
        return nil, fmt.Errorf("遍历用户数据失败: %v", err)
    }
    
    return users, nil
}

// 搜索用户
func (us *UserService) SearchUsers(keyword string) ([]User, error) {
    query := `
    SELECT id, name, email, age, address, created_at, updated_at 
    FROM users 
    WHERE name LIKE ? OR email LIKE ? OR address LIKE ?
    ORDER BY id
    `
    
    searchPattern := "%" + keyword + "%"
    
    rows, err := us.db.Query(query, searchPattern, searchPattern, searchPattern)
    if err != nil {
        return nil, fmt.Errorf("搜索用户失败: %v", err)
    }
    defer rows.Close()
    
    var users []User
    for rows.Next() {
        var user User
        err := rows.Scan(
            &user.ID, &user.Name, &user.Email, &user.Age, 
            &user.Address, &user.CreatedAt, &user.UpdatedAt)
        
        if err != nil {
            return nil, fmt.Errorf("扫描用户数据失败: %v", err)
        }
        
        users = append(users, user)
    }
    
    if err = rows.Err(); err != nil {
        return nil, fmt.Errorf("遍历用户数据失败: %v", err)
    }
    
    return users, nil
}

// 获取用户统计信息
func (us *UserService) GetUserStats() (map[string]int, error) {
    stats := make(map[string]int)
    
    // 总用户数
    var totalUsers int
    err := us.db.QueryRow("SELECT COUNT(*) FROM users").Scan(&totalUsers)
    if err != nil {
        return nil, fmt.Errorf("获取总用户数失败: %v", err)
    }
    stats["total"] = totalUsers
    
    // 按年龄段统计
    ageGroups := []struct {
        name  string
        query string
    }{
        {"young", "SELECT COUNT(*) FROM users WHERE age < 18"},
        {"adult", "SELECT COUNT(*) FROM users WHERE age BETWEEN 18 AND 60"},
        {"senior", "SELECT COUNT(*) FROM users WHERE age > 60"},
    }
    
    for _, group := range ageGroups {
        var count int
        err := us.db.QueryRow(group.query).Scan(&count)
        if err != nil {
            return nil, fmt.Errorf("获取%s用户数失败: %v", group.name, err)
        }
        stats[group.name] = count
    }
    
    return stats, nil
}

// 关闭数据库连接
func (us *UserService) Close() error {
    return us.db.Close()
}

// 显示菜单
func showMenu() {
    fmt.Println("\n===== 用户管理系统 =====")
    fmt.Println("1. 创建用户")
    fmt.Println("2. 查看用户")
    fmt.Println("3. 更新用户")
    fmt.Println("4. 删除用户")
    fmt.Println("5. 列出所有用户")
    fmt.Println("6. 搜索用户")
    fmt.Println("7. 用户统计")
    fmt.Println("8. 退出")
    fmt.Print("请选择操作: ")
}

// 读取用户输入
func readInput(prompt string) string {
    fmt.Print(prompt)
    var input string
    fmt.Scanln(&input)
    return strings.TrimSpace(input)
}

// 读取整数输入
func readIntInput(prompt string) int {
    for {
        input := readInput(prompt)
        value, err := strconv.Atoi(input)
        if err != nil {
            fmt.Println("请输入有效的整数")
            continue
        }
        return value
    }
}

func main() {
    // 创建用户服务
    userService, err := NewUserService("./users.db")
    if err != nil {
        log.Fatalf("创建用户服务失败: %v", err)
    }
    defer userService.Close()
    
    fmt.Println("欢迎使用用户管理系统")
    
    for {
        showMenu()
        choice := readInput("")
        
        switch choice {
        case "1":
            // 创建用户
            name := readInput("姓名: ")
            email := readInput("邮箱: ")
            age := readIntInput("年龄: ")
            address := readInput("地址: ")
            
            user := User{
                Name:    name,
                Email:   email,
                Age:     age,
                Address: address,
            }
            
            id, err := userService.CreateUser(user)
            if err != nil {
                fmt.Printf("创建用户失败: %v\n", err)
            } else {
                fmt.Printf("用户创建成功,ID: %d\n", id)
            }
            
        case "2":
            // 查看用户
            id := readIntInput("用户ID: ")
            user, err := userService.GetUser(id)
            if err != nil {
                fmt.Printf("获取用户失败: %v\n", err)
            } else {
                fmt.Printf("ID: %d\n", user.ID)
                fmt.Printf("姓名: %s\n", user.Name)
                fmt.Printf("邮箱: %s\n", user.Email)
                fmt.Printf("年龄: %d\n", user.Age)
                fmt.Printf("地址: %s\n", user.Address)
                fmt.Printf("创建时间: %s\n", user.CreatedAt.Format("2006-01-02 15:04:05"))
            }
            
        case "3":
            // 更新用户
            id := readIntInput("用户ID: ")
            
            // 先获取现有用户信息
            user, err := userService.GetUser(id)
            if err != nil {
                fmt.Printf("获取用户失败: %v\n", err)
                continue
            }
            
            fmt.Println("当前用户信息:")
            fmt.Printf("姓名: %s\n", user.Name)
            fmt.Printf("邮箱: %s\n", user.Email)
            fmt.Printf("年龄: %d\n", user.Age)
            fmt.Printf("地址: %s\n", user.Address)
            
            // 获取新信息
            name := readInput("新姓名 (留空保持不变): ")
            if name != "" {
                user.Name = name
            }
            
            email := readInput("新邮箱 (留空保持不变): ")
            if email != "" {
                user.Email = email
            }
            
            ageInput := readInput("新年龄 (留空保持不变): ")
            if ageInput != "" {
                age, err := strconv.Atoi(ageInput)
                if err == nil {
                    user.Age = age
                }
            }
            
            address := readInput("新地址 (留空保持不变): ")
            if address != "" {
                user.Address = address
            }
            
            if err := userService.UpdateUser(user); err != nil {
                fmt.Printf("更新用户失败: %v\n", err)
            } else {
                fmt.Println("用户更新成功")
            }
            
        case "4":
            // 删除用户
            id := readIntInput("用户ID: ")
            
            // 确认删除
            confirm := readInput(fmt.Sprintf("确定要删除ID为%d的用户吗? (y/n): ", id))
            if strings.ToLower(confirm) == "y" {
                if err := userService.DeleteUser(id); err != nil {
                    fmt.Printf("删除用户失败: %v\n", err)
                } else {
                    fmt.Println("用户删除成功")
                }
            } else {
                fmt.Println("取消删除")
            }
            
        case "5":
            // 列出所有用户
            users, err := userService.ListUsers()
            if err != nil {
                fmt.Printf("获取用户列表失败: %v\n", err)
                continue
            }
            
            if len(users) == 0 {
                fmt.Println("没有用户")
                continue
            }
            
            fmt.Println("\n用户列表:")
            fmt.Println("ID\t姓名\t邮箱\t年龄")
            for _, user := range users {
                fmt.Printf("%d\t%s\t%s\t%d\n", user.ID, user.Name, user.Email, user.Age)
            }
            
        case "6":
            // 搜索用户
            keyword := readInput("搜索关键词: ")
            users, err := userService.SearchUsers(keyword)
            if err != nil {
                fmt.Printf("搜索用户失败: %v\n", err)
                continue
            }
            
            if len(users) == 0 {
                fmt.Println("没有找到匹配的用户")
                continue
            }
            
            fmt.Printf("\n找到%d个匹配的用户:\n", len(users))
            fmt.Println("ID\t姓名\t邮箱\t年龄")
            for _, user := range users {
                fmt.Printf("%d\t%s\t%s\t%d\n", user.ID, user.Name, user.Email, user.Age)
            }
            
        case "7":
            // 用户统计
            stats, err := userService.GetUserStats()
            if err != nil {
                fmt.Printf("获取用户统计失败: %v\n", err)
                continue
            }
            
            fmt.Println("\n用户统计:")
            fmt.Printf("总用户数: %d\n", stats["total"])
            fmt.Printf("青少年用户 (<18岁): %d\n", stats["young"])
            fmt.Printf("成年用户 (18-60岁): %d\n", stats["adult"])
            fmt.Printf("老年用户 (>60岁): %d\n", stats["senior"])
            
        case "8":
            // 退出
            fmt.Println("感谢使用用户管理系统,再见!")
            return
            
        default:
            fmt.Println("无效的选择,请重新输入")
        }
    }
}

推荐资源

书籍

  • 《Go Web编程》- Web开发实战
  • 《Go语言编程》- 综合应用
  • 《Go语言标准库》- 标准库详解

在线资源

工具

评估方式

功能完整性测试

  1. 文件备份工具:增量备份、恢复功能、备份列表
  2. Web爬虫:并发爬取、链接提取、去重处理
  3. 数据库应用:完整CRUD操作、事务处理

代码质量评估

  • 错误处理完整性
  • 代码可读性和可维护性
  • 性能优化程度
  • 测试覆盖率

时间安排(3-4周)

  • 第1-5天:文件操作与网络编程
  • 第6-10天:数据库操作与数据处理
  • 第11-15天:测试与调试技巧
  • 第16-21天:项目练习与优化

常见问题

Q: 如何处理大文件读写?

A: 使用bufio.Scanner或分块读取,避免内存溢出

Q: 数据库连接池如何配置?

A: 使用sql.DB的SetMaxOpenConns、SetMaxIdleConns等方法配置

Q: 如何调试并发程序?

A: 使用race detector:go run -race main.go

下一步

完成本阶段学习后,进入第四阶段:完整项目实践