Golang中如何实现交互式终端会话:支持用户输入和提问功能

Golang中如何实现交互式终端会话:支持用户输入和提问功能 我正在尝试构建一个打字速度检查器作为练习。我不知道如何建立一个持续的终端会话,首先要求用户登录,然后登录后用户将有一个可以打字的地方。我试图实现类似于NodeJS的REPL的功能。该如何实现? 是否有相关的库?

// 代码示例:一个简单的终端交互循环
package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
    reader := bufio.NewReader(os.Stdin)
    
    fmt.Print("请输入用户名: ")
    username, _ := reader.ReadString('\n')
    username = strings.TrimSpace(username)
    
    fmt.Printf("欢迎, %s! 现在可以开始输入了。\n", username)
    fmt.Println("输入 'exit' 退出程序")
    
    for {
        fmt.Print("> ")
        input, _ := reader.ReadString('\n')
        input = strings.TrimSpace(input)
        
        if input == "exit" {
            fmt.Println("再见!")
            break
        }
        
        fmt.Printf("你输入了: %s\n", input)
    }
}

更多关于Golang中如何实现交互式终端会话:支持用户输入和提问功能的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

谢谢,这正是我一直在寻找的!

更多关于Golang中如何实现交互式终端会话:支持用户输入和提问功能的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang中实现交互式终端会话,可以使用标准库的bufioos包,也可以使用专门的第三方库如github.com/chzyer/readlinegithub.com/c-bata/go-prompt。以下是几种实现方式:

1. 使用标准库实现(基础版)

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
    "time"
)

func login() string {
    reader := bufio.NewReader(os.Stdin)
    
    for {
        fmt.Print("用户名: ")
        username, _ := reader.ReadString('\n')
        username = strings.TrimSpace(username)
        
        if username != "" {
            return username
        }
        fmt.Println("用户名不能为空")
    }
}

func typingSession(username string) {
    reader := bufio.NewReader(os.Stdin)
    fmt.Printf("欢迎 %s! 开始打字测试\n", username)
    fmt.Println("输入 'quit' 退出,'stats' 查看统计")
    
    var totalChars, totalWords int
    startTime := time.Now()
    
    for {
        fmt.Print("> ")
        input, _ := reader.ReadString('\n')
        input = strings.TrimSpace(input)
        
        if input == "quit" {
            break
        }
        
        if input == "stats" {
            duration := time.Since(startTime)
            fmt.Printf("总字符数: %d, 总单词数: %d, 用时: %v\n", 
                totalChars, totalWords, duration)
            continue
        }
        
        // 打字统计
        totalChars += len(input)
        words := strings.Fields(input)
        totalWords += len(words)
        
        fmt.Printf("输入: %s (字符数: %d, 单词数: %d)\n", 
            input, len(input), len(words))
    }
    
    duration := time.Since(startTime)
    fmt.Printf("\n测试结束! 总用时: %v\n", duration)
}

func main() {
    username := login()
    typingSession(username)
}

2. 使用 readline 库(支持历史记录和自动补全)

package main

import (
    "fmt"
    "strings"
    "time"
    
    "github.com/chzyer/readline"
)

func main() {
    // 登录阶段
    rl, err := readline.New("用户名: ")
    if err != nil {
        panic(err)
    }
    defer rl.Close()
    
    username, err := rl.Readline()
    if err != nil {
        return
    }
    username = strings.TrimSpace(username)
    
    // 打字测试阶段
    prompt := fmt.Sprintf("%s> ", username)
    rl, err = readline.NewEx(&readline.Config{
        Prompt:          prompt,
        HistoryFile:     "/tmp/typing_test.history",
        AutoComplete:    completer,
        InterruptPrompt: "^C",
        EOFPrompt:       "exit",
    })
    if err != nil {
        panic(err)
    }
    defer rl.Close()
    
    fmt.Printf("欢迎 %s! 开始打字测试 (Ctrl+C 退出)\n", username)
    
    var totalChars int
    startTime := time.Now()
    
    for {
        line, err := rl.Readline()
        if err != nil { // io.EOF, readline.ErrInterrupt
            break
        }
        
        line = strings.TrimSpace(line)
        if line == "" {
            continue
        }
        
        totalChars += len(line)
        words := len(strings.Fields(line))
        
        fmt.Printf("输入: %s (字符: %d, 单词: %d)\n", 
            line, len(line), words)
    }
    
    duration := time.Since(startTime)
    fmt.Printf("\n测试结束! 总用时: %v, 总字符数: %d\n", 
        duration, totalChars)
}

func completer(line string) []string {
    return []string{
        "stats",
        "quit",
        "help",
    }
}

3. 使用 go-prompt 库(支持更丰富的交互)

package main

import (
    "fmt"
    "strings"
    "time"
    
    "github.com/c-bata/go-prompt"
)

var (
    username      string
    totalChars    int
    sessionStart  time.Time
    inTypingMode  bool
)

func loginExecutor(t string) {
    if strings.TrimSpace(t) == "" {
        fmt.Println("用户名不能为空")
        return
    }
    username = t
    inTypingMode = true
    sessionStart = time.Now()
    fmt.Printf("欢迎 %s! 开始打字测试\n", username)
    fmt.Println("输入 'quit' 退出,'stats' 查看统计")
}

func typingExecutor(t string) {
    if t == "quit" {
        duration := time.Since(sessionStart)
        fmt.Printf("\n测试结束! 总用时: %v, 总字符数: %d\n", 
            duration, totalChars)
        return
    }
    
    if t == "stats" {
        duration := time.Since(sessionStart)
        words := len(strings.Fields(t))
        fmt.Printf("统计 - 字符数: %d, 单词数: %d, 用时: %v\n", 
            totalChars, words, duration)
        return
    }
    
    totalChars += len(t)
    words := len(strings.Fields(t))
    fmt.Printf("输入: %s (字符: %d, 单词: %d)\n", t, len(t), words)
}

func loginCompleter(d prompt.Document) []prompt.Suggest {
    return []prompt.Suggest{}
}

func typingCompleter(d prompt.Document) []prompt.Suggest {
    return []prompt.Suggest{
        {Text: "stats", Description: "查看统计信息"},
        {Text: "quit", Description: "退出程序"},
    }
}

func main() {
    fmt.Println("=== 打字速度测试 ===")
    
    // 登录阶段
    for !inTypingMode {
        p := prompt.New(
            loginExecutor,
            loginCompleter,
            prompt.OptionPrefix("用户名: "),
            prompt.OptionTitle("用户登录"),
        )
        p.Run()
    }
    
    // 打字测试阶段
    prompt.New(
        typingExecutor,
        typingCompleter,
        prompt.OptionPrefix(username + "> "),
        prompt.OptionTitle("打字测试"),
        prompt.OptionAddKeyBind(prompt.KeyBind{
            Key: prompt.ControlC,
            Fn: func(b *prompt.Buffer) {
                duration := time.Since(sessionStart)
                fmt.Printf("\n测试中断! 总用时: %v, 总字符数: %d\n", 
                    duration, totalChars)
            },
        }),
    ).Run()
}

4. 打字速度测试完整示例

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
    "time"
    "unicode"
)

type TypingStats struct {
    Username     string
    StartTime    time.Time
    EndTime      time.Time
    TotalChars   int
    TotalWords   int
    TotalLines   int
    Errors       int
}

func calculateWPM(chars int, duration time.Duration) float64 {
    // 假设平均单词长度为5个字符
    words := float64(chars) / 5.0
    minutes := duration.Minutes()
    if minutes == 0 {
        return 0
    }
    return words / minutes
}

func typingTest() {
    reader := bufio.NewReader(os.Stdin)
    stats := &TypingStats{}
    
    // 登录
    fmt.Print("请输入用户名: ")
    username, _ := reader.ReadString('\n')
    stats.Username = strings.TrimSpace(username)
    
    fmt.Printf("\n欢迎 %s! 开始打字测试\n", stats.Username)
    fmt.Println("输入以下文本 (输入完成后按回车):")
    testText := "The quick brown fox jumps over the lazy dog"
    fmt.Println("\n" + testText + "\n")
    
    stats.StartTime = time.Now()
    fmt.Print("开始输入: ")
    
    userInput, _ := reader.ReadString('\n')
    userInput = strings.TrimSpace(userInput)
    
    stats.EndTime = time.Now()
    stats.TotalChars = len(userInput)
    
    // 计算单词数
    stats.TotalWords = len(strings.Fields(userInput))
    
    // 计算错误数
    for i := 0; i < len(userInput) && i < len(testText); i++ {
        if unicode.ToLower(rune(userInput[i])) != unicode.ToLower(rune(testText[i])) {
            stats.Errors++
        }
    }
    
    // 显示结果
    duration := stats.EndTime.Sub(stats.StartTime)
    accuracy := 100.0
    if stats.TotalChars > 0 {
        accuracy = 100.0 - (float64(stats.Errors)/float64(stats.TotalChars))*100.0
    }
    
    fmt.Printf("\n=== 测试结果 ===\n")
    fmt.Printf("用户名: %s\n", stats.Username)
    fmt.Printf("用时: %v\n", duration)
    fmt.Printf("字符数: %d\n", stats.TotalChars)
    fmt.Printf("单词数: %d\n", stats.TotalWords)
    fmt.Printf("WPM: %.2f\n", calculateWPM(stats.TotalChars, duration))
    fmt.Printf("准确率: %.2f%%\n", accuracy)
    fmt.Printf("错误数: %d\n", stats.Errors)
}

func main() {
    typingTest()
}

这些示例展示了不同复杂度的实现方式。标准库适合简单场景,readline提供历史记录和自动补全,go-prompt提供更丰富的终端交互功能。对于打字速度检查器,建议使用readline库,它在功能和复杂度之间提供了良好的平衡。

回到顶部