2014-11-02 52 views
4

管数据到调用scanf函数(程序)和read()有一个C程序,它看起来是这样的:如果使用像我如何在Linux

命令我管数据到这个程序

#include <stdio.h> 
#include <unistd.h> 

int main() 
{ 
    int n; 
    char str[16]; 
    scanf("%d", &n); 
    printf("n: %d\n", n); 

    int count = read(STDIN_FILENO, str, 16); 
    printf("str: %s\n", str); 
    printf("read %d bytes\n", count); 
} 

(echo -en '45\n'; echo -en 'text\n') | ./program

只有scanf()实际读取数据。 read()只读0个字节。

换句话说,方案产出

n: 45 
str: 
read 0 bytes

我怎么能管到的数据都scanf()read()

编辑:对不起,我应该澄清:我正在寻找一种方法来做到这一点,而无需修改源代码。感谢无论如何回答和评论的人。

+0

你可以先加入:#include 到你的代码。 – user3629249 2014-11-02 09:11:53

+0

scanf()的返回代码需要检查以确保操作实际将值输入到'n'中。 – user3629249 2014-11-02 09:14:40

+0

注意:read()不会在输入末尾放置空字符。所以代码必须这样做,使用来自read()的调用的返回值来确定在哪里放置空字符。否则,printf将直接读取实际的str内容,直到它(最终)遇到空字符或导致seg错误 – user3629249 2014-11-02 09:20:52

回答

3

对于同一文件描述符,不推荐使用文件描述符函数(读取)和流函数(scanf)。使用FILE *(即fread/fprintf/scanf/...)的函数正在缓冲数据,而使用文件描述符(即read/write/...)的函数不使用这些缓冲区。在你的情况下,修复程序最简单的方法是使用fread而不是read。该程序可能看起来像:

#include <stdio.h> 
#include <unistd.h> 

int main() { 
    int n; 
    char str[16]; 
    scanf("%d", &n); 
    printf("n: %d\n", n); 

    int count = fread(str, 16, 1, stdin); 
    printf("str: %s\n", str); 
    printf("read %d bytes\n", count); 
} 

在你原来的例子,scanf已经提前读取输入并存储在其缓冲区。由于输入非常短,并且立即可用,因此完全读取到缓冲区,并且您的调用read没有更多可读的内容。

当您直接从终端输入输入时,不会发生这种情况,因为scanf在从终端设备读取时不会将数据超过一行进行缓冲。

(echo -en '45\n'; sleep 1; echo -en 'text\n') | ./program 
+0

有没有办法在不修改源代码的情况下做到这一点?可能有一些方法可以确保'scanf'不会缓冲所有输入? – IGNUcius 2014-11-02 09:21:05

+0

@IGNUcius:这不是缓冲的调用,它是用于扫描/读取的结果:'FILE'。 – alk 2014-11-02 09:28:54

+0

这可能是更值得更明确地表明,基本概念是问题:缓冲I/O与无缓冲I/O – alk 2014-11-02 09:30:08

2
#include <stdio.h> 
#include <unistd.h> 

int main() 
{ 
    int n; 
    char str[16]; 
    setbuf(stdin, NULL); // Ensure that there's no buffering for stdin 
    scanf("%d", &n); 
    printf("n: %d\n", n); 

    int count = read(STDIN_FILENO, str, 16); 
    printf("str: %s\n", str); 
    printf("read %d bytes\n", count); 
} 

正如前面说的答案,scanf函数导致您的问题,因为它使用了一个缓冲区:这也不会,如果你通过一个命令创建一个在您例如管道输入一次暂停发生。所以你可以确保它不通过调用setbuf(stdin,NULL)