理解c中的read + write

时间:2016-04-01 06:43:39

标签: c stdin read-write

char buf[1];
if (argc == 1) {
    while (read(STDIN_FILENO, buf, 1) > 0) {
        write(1, buf, sizeof(buf));
    }
}

我想澄清一些有关此代码段的内容。我们运行它./exec_file假设我们只需按Enter键。我们移动到下一行并读取1个字节'\n'然后将其写入stdout,使我们再下一行......简单到了。现在我们假设输入h,然后输入。该程序会使用不可见的h在下一行显示'\n'

在我们输入h之后查看代码,它将其读入缓冲区然后将其写入stdout,但不知何故,程序等待在下一行吐出来,直到我按下Enter ...之后?

最后,当我们第一次点击while循环时,最初不会返回0,因为我们最初没有输入任何东西?

3 个答案:

答案 0 :(得分:3)

stdin与大多数其他流的行为略有不同。

首先,输入是行缓冲的。这意味着在您按Enter键之前输入不可用。这解释了h赢了,直到你按Enter键为止。

因为它是一个流,所以它并没有真正结束。当没有数据要读取时,呼叫将阻塞,直到某些数据可用(或直到程序收到信号)。套接字的工作方式相同。

使用fcntl阻止行为can be turned off

int fd = STDIN_FILENO;
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);

答案 1 :(得分:1)

默认情况下,终端行缓冲,因为它处于规范模式。来自Linux手册tcgetattr(3)

  

规范和非规范模式

     

ICANON佳能的设定   c_lflag中的flag确定终端是否正在运行   规范模式(ICANON设置)或非规范模式(ICANON未设置)。   默认情况下,ICANON设置。

     

在规范模式中:

     
      
  • 输入逐行可用。输入行是   可得到    当键入其中一个行分隔符时(NL,EOL,EOL2;或EOF at。)    行的开头)。除了EOF的情况,行分隔符    包含在read(2)返回的缓冲区中。

  •   
  • 启用行编辑(ERASE,KILL;如果IEXTEN标志为   组:    WERASE,REPRINT,LNEXT)。 read(2)最多返回一行    输入;如果read(2)请求的字节数少于可用的字节数    当前的输入行,然后只有所请求的字节数    阅读,剩下的字符将来可用    读(2)。

  •   

您可以通过使用正确的标记调用tcgetattr来关闭终端上的规范模式。首先禁用规范模式;然后将超时设置为0;将最小读数设置为1以阻止读取,将0设置为非阻塞读取。通常习惯上也会禁用本地回声,否则您键入的所有内容仍然会自动显示(并在程序中显示两次):

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

int main() {
    struct termios old_settings, new_settings;
    int is_terminal;

    // check whether the stdin is a terminal to begin with
    if (is_terminal = isatty(STDIN_FILENO)) {     
        // get the old settings
        tcgetattr(STDIN_FILENO, &old_settings);

        new_settings = old_settings;

        // disable canonical mode and echo
        new_settings.c_lflag &= (~ICANON & ~ECHO);

        // at least one character must be written before read returns
        new_settings.c_cc[VMIN] = 1;

        // no timeout
        new_settings.c_cc[VTIME] = 0;

        tcsetattr(STDIN_FILENO, TCSANOW, &new_settings);
    }

    while (read(STDIN_FILENO, buf, 1) > 0) {
        // add this here so that you can verify that it is character by character,
        // and not the local echo from the terminal
        write(STDOUT_FILENO, ">", 1);
        write(STDOUT_FILENO, buf, sizeof(buf));
    }

    // finally restore the old settings if it was a terminal
    if (is_terminal) {
        tcsetattr(STDIN_FILENO, TCSANOW, &old_settings);
    }
    return 0;
}

答案 2 :(得分:0)

如果您仍希望阻止发生,但希望逐个字符地阅读,则可以使用termios来配置如何将输入提供给您的程序。请参阅下面的代码。

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

int main()
{
    char buf[1];
    struct termios term, term_orig;

    if (tcgetattr(0, &term_orig)) {
        printf("tcgetattr failed\n");
        exit(-1);
    }

    term = term_orig;

    term.c_lflag &= ~ICANON;
    term.c_lflag |= ECHO;
    term.c_cc[VMIN] = 1;
    term.c_cc[VTIME] = 0;

    if (tcsetattr(0, TCSANOW, &term)) {
        printf("tcsetattr failed\n");
        exit(-1);
    }

    while (read(0, buf, 1) > 0) {
        write(1, buf, sizeof(buf));
    }
    return 0;
}