** Go 开发过程中,必不可少会需要与用户交互的操作,使用 bufio 读取字符串用户输入也是一种常见方式,但是读入后的字符串需要经过一系列的操作才能转换为只包含数字的字符串。**
- 在使用如下代码将输入数据转换为字符串时,会出现格式错误
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
)
func main() {
maxNum := 100
secretNumber := rand.Intn(maxNum)
fmt.Println("The secret Number is ", secretNumber)
fmt.Println("Please input your guess")
reader := bufio.NewReader(os.Stdin) // 使用 os.Stdin 来得到 stdin 文件,进而使用 bufio.NewReader 来把 stdin 文件转化为一个 reader 变量, reader 变量中有许多操作一个流的操作
input, err := reader.ReadString('\n') // 读取一行, reader.ReaderString 会以参数出现的第一个位置作为终止位置,读取一行(包含参数)
if err != nil {
fmt.Println("An error occured while reading input.Please try again", err)
return
}
input = strings.TrimSuffix(input, "\n") // 去掉字符串末尾的换行符 "\n"
guess, err := strconv.Atoi(input) // 将输入数字串转换为 int 型变量
if err != nil {
fmt.Println("Invalid input. Please enter an integer value")
return
}
fmt.Println("You guess is", guess)
}
-
但是这样会出现 "Invalid input" 错误,也就是在将输入字符转换为数字的过程中出错。即输入数字不合法。所以可以输出 input 字符串的每个字符来看下输入字符在被读入处理后变成了什么。如下图所示,输入的数字 "666",被处理后得到的每个字符的 rune 值为 54 54 54 13 10。
可以看出即使被处理后字符串仍然存在换行符"\n" 和回车符"\r"。在使用方法 strings.TrimSuffix 去掉 input 尾部的所有 "\n" 时,由于 input 结尾是 "\r",所以不会删除任何字符,最终转换数字的字符串为 "666\n\r",所以 strconv.Atoi 方法就会返回异常。那究竟是什么原因呢?
-
这是由于不同操作系统对回车键的处理是不同的。
-
Unix 系统里,每行结尾只有 "<换行>",即 "\n"
-
Windows 系统里,每行结尾是 "<回车><换行>",即 "\r\n"
-
Mac 系统里,每行结尾是 "<回车>", 即 "\r".
这样导致的结果就是,Unix/Max 系统下的文件在 Windows 里打开的话,所有文件会变成一行;而 Windows 里的文件在 Unix/Max 系统中打开的话,在每行的末尾可能多出一个 ^M 符号。 并且,在 Windows 系统中回车键被当作 "\r\n" 的组合来处理,当我们键入回车时,Windows 系统会把回车键解读为 "\r\n"; Unix 系统只会当作 "\n" 来处理。
- 代码改进:如果是 Windows 系统的话,就要在读到每个 "\r" 时停止,并且将 input 末尾的 "\r" 删除,即将上述代码中第 20 行和第 26 行的 "\n" 换为 "\r" 即可。如果要连续读取多行时,还要注意将前面 "\r" 后面的 "\n" 删除,否则会在读入下一行的开头。
input, err := reader.ReadString('\r')
input = strings.TrimSuffix(input, "\r")