# 第三阶段:Go语言实战模块 ## 学习目标 - 掌握Go语言在实际开发中的应用 - 理解文件操作、网络编程、数据库连接 - 能够开发完整的应用程序 - 掌握测试和调试技巧 ## 详细学习内容 ### 3.1 文件I/O操作 ```go 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 网络编程 ```go 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 ") 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 数据库操作 ```go 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: "zhangsan@example.com", Age: 25}, {Name: "李四", Email: "lisi@example.com", Age: 30}, {Name: "王五", Email: "wangwu@example.com", 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数据处理 ```go 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: "zhangsan@example.com", Age: 25, CreatedAt: time.Now()}, {ID: 2, Name: "李四", Email: "lisi@example.com", Age: 30, CreatedAt: time.Now()}, {ID: 3, Name: "王五", Email: "wangwu@example.com", 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 测试与调试 ```go 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:文件备份工具 ```go 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爬虫 ```go 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(`(.*?)`) 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(`]+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应用 ```go 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语言标准库》- 标准库详解 ### 在线资源 - [Go官方博客](https://blog.golang.org/) - 官方技术文章 - [Go语言中文网](https://studygolang.com/) - 中文社区资源 - [Awesome Go](https://awesome-go.com/) - Go优秀项目集合 ### 工具 - [Postman](https://www.postman.com/) - API测试 - [MySQL Workbench](https://www.mysql.com/products/workbench/) - 数据库管理 - [Wireshark](https://www.wireshark.org/) - 网络分析 ## 评估方式 ### 功能完整性测试 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 ## 下一步 完成本阶段学习后,进入第四阶段:完整项目实践