1

这是一个使用 bufio.Scanner 和 fmt.Scanln 的简单程序,它按预期工作:

scanner := bufio.NewScanner(os.Stdin)
var s string
fmt.Println("Enter a string:")
scanner.Scan()
s = scanner.Text()
fmt.Printf("You entered: %q\n\n", s)

var i int
fmt.Println("Enter an int:")
fmt.Scanln(&i)
fmt.Printf("You entered: %d\n", i)

这是程序输出(括号内的文字[]是键盘输入)
Enter a string:
[mary had a little lamb]
You entered: "mary had a little lamb"

Enter an int: 
[42]
You entered: 42



现在,我们不使用默认的标准输入,而是使用管道从字符串而不是键盘重定向输入。

readStdin, writeStdin, err := os.Pipe()
if err != nil {
    fmt.Println("Pipe Error:", err)
    return
}

os.Stdin = readStdin

writeStdin.WriteString("mary had a little lamb\n42\n")

scanner := bufio.NewScanner(os.Stdin)
var s string
fmt.Println("Enter a string:")
scanner.Scan()
s = scanner.Text()
fmt.Printf("You entered: %q\n\n", s)

var i int
fmt.Println("Enter an int:")
fmt.Scanln(&i)
fmt.Printf("You entered: %d\n", i)


readStdin.Close()

这是程序输出:

Enter a string:
You entered: "mary had a little lamb"

Enter an int:

在“Enter an int:”提示后,程序无限期等待,必须手动终止。:(

奇怪的是,如果我颠倒事件的顺序(fmt.Scanln 在 bufio.Scanner.Scan 之前),那么它会完美运行。如果我专门使用 fmt.Scanln 或专门使用 bufio.Scanner.Scan,它也可以正常工作。

所有这些都让我想到:

a) Go 处理键盘输入的方式有一些独特的不同
b) 使用 bufio.Scanner.Scan 扫描时我应该使用其他类型的分隔符
c) 永远不要同时使用这两种扫描机制(请不要不要让这成为答案!)

4

1 回答 1

1

bufio.Scanner使用缓冲区读取输入块,然后返回该缓冲区的部分。当您使用管道编写完整的输入时,bufio.Scanner只需读取整个输入,并返回由换行符分隔的两个标记。fmt.Scanln将始终从标准输入读取。到时间fmt.Scanln运行时,bufio.Scanner将已经读取完整的输入,所以fmt.Scanln只需等待。如果您在开头设置 sleep 并在程序开始读取之前键入所有输入,第一个程序也会发生同样的事情。所以这与 Go 如何处理输入或扫描分隔符无关。

如果你使用缓冲 i/o,你必须期望缓冲区有比你需要的更多的东西。因此,请勿将两者混合使用,或fmt.Scanln在完全使用扫描仪后使用。

于 2020-08-02T03:58:07.313 回答