【问题标题】:Understanding read + write in c理解c中的读+写
【发布时间】:2016-04-01 06:43:39
【问题描述】:
char buf[1];
if (argc == 1) {
    while (read(STDIN_FILENO, buf, 1) > 0) {
        write(1, buf, sizeof(buf));
    }
}

关于这个 sn-p,我有几件事想澄清一下。我们运行它, ./exec_file 假设我们只是按 Enter 键。我们移至下一行并读取 1 个字节 '\n' 然后将其写入标准输出,从而使我们再下一行……很简单。现在假设我们输入h,然后输入。程序在下一行吐出h,并带有一个不可见的'\n'

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

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

【问题讨论】:

  • 标准输入是行缓冲的;在用户按下回车键之前,输入不会提交给您的程序。
  • 啊所以如果我们有hhh 然后输入它形成一个包含hhh\n 的文件然后读取

标签: c stdin read-write


【解决方案1】:

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

首先,输入是行缓冲的。这意味着在您按下回车之前输入不可用。这解释了h 在您按下回车之前不会出现。

因为它是一个流,它并没有真正的结束。当没有数据要读取时,调用不会失败,而是会阻塞直到有数据可用(或直到程序接收到信号)。套接字的工作方式相同。

阻塞行为can be turned off使用fcntl

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

【讨论】:

    【解决方案2】:

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

    规范和非规范模式

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

    在规范模式下:

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

    • 启用行编辑(ERASE、KILL;如果 IEXTEN 标志为 放: WERASE,重印,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;
    }
    

    【讨论】:

      【解决方案3】:

      如果您仍然希望阻塞发生,但想逐个字符地读取,您可以使用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;
      }
      

      【讨论】:

      • 使用isatty(STDIN_FILENO)看看这个方法是否有效,因为标准输入不一定是终端。
      猜你喜欢
      • 1970-01-01
      • 2012-12-28
      • 2015-02-08
      • 2014-05-21
      • 1970-01-01
      • 2010-09-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多