Search code examples
gouser-inputstdin

How to flush Stdin after fmt.Scanf() in Go?


Here's an issue that's bedeviling me at the moment. When getting input from the user, I want to employ a loop to ask the user to retry until they enter valid input:

// 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)    
}

Running the above, if the user enters valid input, no problem:

Please enter an integer: 

3

3

exit code 0, process exited normally.

But try inputting a string instead?

Please enter an integer: 
what?
Sorry, invalid input. Please enter an integer:

Sorry, invalid input. Please enter an integer:

Sorry...

Etc, and it keeps looping character by character until the string is exhausted. Even inputting a single character loops twice, I assume as it parses the newline.

Anyways, there must be a way to flush Stdin in Go?

P.S. In the absence of such a feature, how would you work around it to provide equivalent functionality? I've failed even at that...


Solution

  • I would fix this by reading until the end of the line after each failure. This clears the rest of the text.

    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)
    }