fmt.Scanf 不消耗字符串末尾的所有字符

fmt.Scanf does not consume all the characters through the end of the string

package main
import "fmt"

func main(){
    var i int
    fmt.Print("input integer: ")
    fmt.Scanf("%d", &i)

}

当我从终端运行这个小程序并给出输入时

input integer: 3.ls

它也执行ls命令 或者当我输入

input integer: 665.cd someDir

它执行 cd someDir。 go.This 中 Scanf 的这种正常行为在 C 中不会发生吗? 谁能解释一下这是怎么回事。

您的 fmt.Scanf("%d", &i) 调用仅从输入中解析一个整数,它 不会 消耗输入直到行尾。

如果输入3.ls,则3被解析为十进制数,.被消耗,将停止扫描。您的应用程序到此结束,因此其余部分(ls 和换行符)将由您的 shell.

执行

使用bufio.Scanner阅读,例如:

fmt.Print("input string: ")
scanner := bufio.NewScanner(os.Stdin)
if !scanner.Scan() {
    return // no input
}
var i int
fmt.Sscanf(scanner.Text(), "%d", &i)
fmt.Println("Entered:", i)

查看相关:

不是答案,只是对@icza 所写内容的扩展,但太大而无法放入评论。

我也会尝试阐明原因

does not happen in C

效果发生。

很可能你用过scanf(3), and the functions of its ilk operate not on file descriptors (like syscall wrappers such as read(2) and write(2)) but rather on so-called "streams" provided by the C standard library; these facilities are referred to as "stdio"

这些流提供默认缓冲,stdin 通常默认为行缓冲。

考虑以下程序:

#include <stdio.h>

int main()
{
    int rc, i;

    printf("input integer: ");

    rc = scanf("%d", &i);
    if (rc != 1) {
        perror("scan failed");
        return 1;
    }

    return 0;
}

如果我们建造它

$ gcc -o scanf -W -Wall -Werror scanf.c

然后 运行 “按原样”,我们观察到程序已经消耗了那些额外的 .ls 个字符:

$ ./scanf
input integer: 3.ls
$

现在让我们看看当我们修改链接到我们程序的 libc 默认使用缓冲的想法时会发生什么。我们将为此使用 stdbuf

$ stdbuf -i 0 ./scanf
input integer: 3.ls
scanf scanf.c
$

这就是@icza 的建议起作用的原因:在 Go 中,变量形成 os 包,其中保存向标准流打开的文件对象没有缓冲,因为它们是 *os.File 类型,并且这些是 OS.

返回的文件描述符的非常薄的包装器

如果你需要缓冲,你应该明确地在它所属的地方使用它(并且要小心:不要在缓冲包装器和它的底层数据上混合 reads/writes - 至少除非你绝对确定你在做什么)。