Golang中为什么会出现打印两次的情况?

Golang中为什么会出现打印两次的情况? 大家好!我是Go语言的新手,所以我想,还有什么比创建一个猜数字游戏更好的学习方式呢!

进展相当顺利,能够解析用户输入……但有一件事我实在无法理解。 我有以下代码:

package main

import (
	"fmt"
	"math/rand"
	"strconv"
)

func randRangeInt(min int, max int) int {
	return min + rand.Intn(max-min)
}

func validateInput(input string) (int) {
	result, err := strconv.Atoi(input)

	if err != nil {
		return -1
	} else {
		return result
	}
}

func main() {
	var secret int = randRangeInt(0, 100)
	var userGuess string

	fmt.Println("I'm thinking of a number between 0 and 100. Try guessing it:")

	fmt.Scanf("%s", &userGuess)

	result := validateInput(userGuess)

	for result < 0 {
		fmt.Println("Try again. Between 0 and 100.")
		fmt.Scanf("%s", &userGuess)
		result = validateInput(userGuess)
	}

	if result > -1 {
		switch {
		case result < secret:
			fmt.Println("Your guess is too low")
		case result > secret:
			fmt.Println("Your guess is too high")
		case result == secret:
			fmt.Println("That's it!")
		}
	} else {
		panic("Something went wrong")
	}
}

在主函数中有一个检查,如果从 validateInput 函数返回的结果小于0(意味着转换时出错),它会再次提示用户输入其他内容。这就是我遇到的问题。当我故意输入非数字内容时,它会提示两次,像这样:

Screenshot 2024-03-01 085445

有人能告诉我这是为什么吗?

我知道这可能是个愚蠢的问题,但就像我说的,我刚开始使用Go,这让我很困惑。感谢任何愿意提供帮助的人 🙂

编辑1:我并不是特别想要更好的错误处理或类似的东西(虽然很感谢)。我只是想知道为什么它会打印两次。


更多关于Golang中为什么会出现打印两次的情况?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

9 回复

这可能与 Windows PowerShell 的工作方式有关。如果你用的是 Mac,可能在使用 zsh?

更多关于Golang中为什么会出现打印两次的情况?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这很奇怪。这是我所有的代码,所以如果你无法复现,我也不知道发生了什么。也许是我的 PowerShell 出了问题。

Aronk112:

如果你用的是Mac,可能在使用zsh吧?

是的,我在Mac上使用的是zsh。

Yamil_Bracho:

我得到了和 Aronk112 相同的结果。

嗯……非常有趣。

我的系统是运行 Ventura 的 MacBook Air,测试是通过终端窗口进行的。

我与 Aronk112 得到了相同的结果。我猜它也在验证回车键,所以你可能需要清空标准输入缓冲区。可以尝试使用 fmt.Scanf("%s\r ", &userGuess)。另外,我使用的是 Windows 系统…

fmt.Scanf("%s\r ", &userGuess)

我无法复现你的问题。

 $ go run main.go
I'm thinking of a number between 0 and 100. Try guessing it:
-7
Try again. Between 0 and 100.
-100
Try again. Between 0 and 100.
88
Your guess is too high

快速浏览了你的代码后,我无法立即看出为什么会打印两次提示信息。

为了尽可能准确地复现你的结果,我输入了 f 作为我的第一个猜测,和你完全一样。

% go run ./main.go
I'm thinking of a number between 0 and 100. Try guessing it:
f
Try again. Between 0 and 100.
50
Your guess is too low
%

抱歉,这并没有复现出你得到的结果。

我还使用有效的数字作为输入运行了程序,同样,每个响应提示也只出现了一次,而不是两次。这项测试包括猜中了正确的数字。

就是这样。我猜是 Scanfgo run ./main.go 之后保留了对回车键的引用(或者它在编译时仍然在内存中?我不太清楚 Go 是如何处理这些事情的。它的内部机制和我习惯的非常不同,所以如果我说了胡话请见谅,而且英语不是我的母语)。通过将第一个 fmt.Scanf("%s", &userGuess) 改为 fmt.Scanf("%s\r", &userGuess),它就能正常工作了。感谢你的建议!

编辑3(还是4?)我用一个预设值进行了测试来对比,它仍然会打印两次,所以这不可能是为了验证 f 字符而按下的回车键(就像我的例子中那样)。

func main() {
    fmt.Println("hello world")
}

问题出在 fmt.Scanf("%s", &userGuess) 的缓冲处理上。当输入非数字内容时,validateInput 返回 -1,但输入缓冲区中仍保留着换行符(\n),导致下一次 fmt.Scanf 立即读取到空字符串,从而再次触发错误提示。

以下是修复后的代码示例:

package main

import (
	"bufio"
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"strings"
)

func randRangeInt(min int, max int) int {
	return min + rand.Intn(max-min)
}

func validateInput(input string) int {
	result, err := strconv.Atoi(strings.TrimSpace(input))
	if err != nil {
		return -1
	}
	return result
}

func main() {
	rand.Seed(time.Now().UnixNano())
	var secret int = randRangeInt(0, 100)
	scanner := bufio.NewScanner(os.Stdin)

	fmt.Println("I'm thinking of a number between 0 and 100. Try guessing it:")

	for scanner.Scan() {
		userGuess := scanner.Text()
		result := validateInput(userGuess)

		if result < 0 {
			fmt.Println("Try again. Between 0 and 100.")
			continue
		}

		switch {
		case result < secret:
			fmt.Println("Your guess is too low")
		case result > secret:
			fmt.Println("Your guess is too high")
		case result == secret:
			fmt.Println("That's it!")
			return
		}
		fmt.Println("Guess again:")
	}
}

关键修改:

  1. 使用 bufio.Scanner 替代 fmt.Scanf,它会自动处理换行符
  2. 通过 strings.TrimSpace() 清理输入
  3. 简化了循环逻辑,避免重复的输入调用

这样就能确保每次提示只出现一次,同时正确处理各种输入情况。

回到顶部