5

这是目前困扰我的一个问题。当从用户那里获得输入时,我想使用一个循环来要求用户重试,直到他们输入有效的输入:

// user_input.go
package main

import (
    "fmt"
)

func main() {
    fmt.Println("Please enter an integer: ")

    var userI int

    for {
        _, err := fmt.Scanf("%d", &userI)
        if err == nil {
            break
        }
        fmt.Println("Sorry, invalid input. Please enter an integer: ")
    }

    fmt.Println(userI)    
}

运行上面,如果用户输入有效的输入,没问题:

请输入整数:<br> 3

3

退出代码0,进程正常退出。

但是尝试输入一个字符串呢?

请输入一个整数:<strong>什么?
抱歉,输入无效。请输入一个整数:

对不起,输入无效。请输入一个整数:

对不起...

等等,它会逐个字符地循环,直到字符串用完。即使输入一个字符循环两次,我假设它解析换行符。

无论如何,一定有办法在 Go 中刷新 Stdin?

PS 如果没有这样的功能,您将如何解决它以提供等效的功能?我什至都失败了...

4

6 回答 6

4

我会通过在每次失败后阅读直到行尾来解决这个问题。这将清除文本的其余部分。

package main

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

func main() {
    stdin := bufio.NewReader(os.Stdin)

    fmt.Println("Please enter an integer: ")

    var userI int

    for {
        _, err := fmt.Fscan(stdin, &userI)
        if err == nil {
            break
        }

        stdin.ReadString('\n')
        fmt.Println("Sorry, invalid input. Please enter an integer: ")
    }

    fmt.Println(userI)
}
于 2013-02-01T06:19:05.360 回答
3

唤醒一个老问题是不是很糟糕?

我更喜欢使用fmt.Scanln,因为 A)它不需要导入另一个库(例如阅读器)和 B)它不涉及显式 for 循环。

func someFunc() {
    fmt.Printf("Please enter an integer: ")

    // Read in an integer
    var i int
    _, err := fmt.Scanln(&i)
    if err != nil {
            fmt.Printf("Error: %s", err.Error())

            // If int read fails, read as string and forget
            var discard string
            fmt.Scanln(&discard)
            return
    }
    fmt.Printf("Input contained %d", i)
}

但是,似乎应该有一个更优雅的解决方案。特别是在 fmt.Scanln 的情况下,读取在第一个非数字字节之后停止而不是“扫描行”似乎很奇怪。

于 2015-06-16T21:29:20.543 回答
1

我知道这已经得到解答,但这是我的实现:

func flush (reader *bufio.Reader) {
    var i int
    for i = 0; i < reader.Buffered(); i++ {
        reader.ReadByte()
    }
}

这应该适用于所有情况,包括不能使用“stdin.ReadString('\n')”的情况。

于 2013-02-25T07:38:13.043 回答
1

我在获取用户输入时遇到了类似的问题,但以稍微不同的方式解决了它。添加到线程以防其他人发现这很有用:

package main

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

// Get first word from stdin
func getFirstWord() (string) {
    input := bufio.NewScanner(os.Stdin)
    input.Scan()
    ans := strings.Fields(input.Text())

    if len(ans) == 0 {
        return ""
    } else {
        return ans[0]
    }
}

func main() {
    fmt.Printf("Would you like to play a game?\n> ")
    ans := getFirstWord()
    fmt.Printf("Your answer: %s\n", ans)
}
于 2016-09-23T07:07:38.373 回答
0

很抱歉挖掘了这个备份,但我今天遇到了这个问题,想通过使用新的标准库功能来改进现有的答案。

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

func discardBuffer(r *bufio.Reader) {
    r.Discard(r.Buffered())
}

stdin := bufio.NewReader(os.Stdin)
var i int
for true {
    if _, err := fmt.Fscanln(stdin, &i); err != nil {
        discardBuffer(stdin)
        // Handle error, display message, etc.
        continue
    }
    // Do your other value checks and validations
    break
}

基本思想是始终缓冲来自标准输入的读取。当您在扫描时遇到错误时,只需丢弃缓冲区内容即可。这样您就可以从一个空缓冲区开始进行下一次扫描。

或者,您可以在扫描之前丢弃缓冲区,这样用户之前的任何杂散输入都不会被拾取。

func fscanln(r *bufio.Reader, a ...interface{}) error {
    r.Discard(r.Buffered())
    _, err := fmt.Fscanln(r, a...)
    return err
}

stdin := bufio.NewReader(os.Stdin)
var i int
if err := fscanln(stdin, &i); err != nil {
    // Handle error
}
于 2017-02-05T07:39:54.533 回答
0

我使用这个片段来过滤不必要的前导空格/新行

in := bufio.NewReader(os.Stdin)
result, err = in.ReadString('\n')
for len(strings.TrimSpace(result)) == 0 {
    result, err = in.ReadString('\n')
}
于 2021-05-25T17:10:18.310 回答