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中实现交互式终端会话,可以使用标准库的bufio和os包,也可以使用专门的第三方库如github.com/chzyer/readline或github.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库,它在功能和复杂度之间提供了良好的平衡。

