I'm working through the Coursera Go course ... it's pretty good.
In my code below, it works fine when the user correctly a float. But when they enter some random string, it prints the error message and prompt again for each character. Why is that?
package main
import "fmt"
func readFloat(title string) float64 {
var userInput float64
for {
fmt.Println("Please enter a float: ")
_, err := fmt.Scanf("%f", &userInput)
if err != nil {
fmt.Printf("Wooops! That's not a float
")
} else {
return userInput
}
}
}
func main() {
var f float64
f = readFloat("acceleration")
fmt.Printf("You entered: %.04f
", f)
}
~/src/coursera/go/course-2-functions-methods/week2 $ go run so.go
Please enter a float:
33.3
You entered: 33.3000
~/src/coursera/go/course-2-functions-methods/week2 $ go run so.go
Please enter a float:
sdf
Wooops! That's not a float
Please enter a float:
Wooops! That's not a float
Please enter a float:
Wooops! That's not a float
Please enter a float:
Wooops! That's not a float
Please enter a float:
The problem is that if fmt.Scanf()
encounters an invalid input, it will stop consuming it. E.g. you want to parse a float using %f
, but if user inputs sdf
, fmt.Scanf()
will know it's invalid after the first s
character, so returns an error and will not consume the rest.
So the next loop iteration begins, it consumes the second character d
, which is again invalid etc.
If you try to enter s2
for example, this will become clear:
Please enter a float:
s2
Wooops! That's not a float
Please enter a float:
You entered: 2.0000
First s
char is invalid, but the next iteration will parse 2
.
One way to "fix" this is to read a whole line as a string, then attempt to parse a float out of the string. For reading a line, use bufio.Scanner
, for parsing a float, use strconv.ParseFloat()
.
Here's an example how you can do that:
func readFloat(title string) float64 {
scanner := bufio.NewScanner(os.Stdin)
for {
fmt.Printf("Please enter a float for %s: ", title)
if !scanner.Scan() {
fmt.Println("Error readling line")
return 0 // Should return an error too!
}
line := scanner.Text()
userInput, err := strconv.ParseFloat(line, 64)
if err == nil {
return userInput
}
fmt.Printf("Wooops! That's not a float
")
}
}
That's because your for loop is an infinite loop if user enters a wrong value. You should have a return
statement after your fmt.Printf("you entered wrong number")
, so that the for loop can exit.