【问题标题】:Can I access stdin without keyboard?我可以在没有键盘的情况下访问标准输入吗?
【发布时间】:2015-09-13 07:35:59
【问题描述】:

我用 gcc 编译了以下代码

int main() {

  int a = 0;

  fprintf( stdin, "%d", 123 );
  fscanf( stdin, "%d", &a );
  printf( "%d\n", a );

  return 0;
}

在我的预期中,程序应该直接执行(即程序永远不会暂停并等待用户输入)。但它仍然停止,等待我的输入。

我想知道当我尝试写一些东西到标准输入时会发生什么以及如何修改这段代码并且它可以直接执行?

【问题讨论】:

  • 你想达到什么目的?如果在某些情况下您想使用“123”而不是标准输入的输入,您应该编写一个在这种情况下简单地返回“123”的函数,否则返回标准输入的值。不要试图以艰难的方式做到这一点。

标签: c linux keyboard stdin


【解决方案1】:

stdin 仅用于输入,stdout 用于输出。 (4566976 的答案向您展示了当您尝试输出到 stdin 时会发生什么)例如参见 glibc documentation on standard streams

(简而言之,写信给stdin 一点意义都没有)

【讨论】:

    【解决方案2】:

    如果打印出fprintf(stdin的返回值,可以看到函数调用失败。

    在 shell 中,您可以通过管道将某些内容输入进程的stdin

    #include <stdio.h>
    
    int main(void) {
    
      int a = 0, ret;
    
      printf("%d\n", ret = fprintf( stdin, "%d", 123 ));
      if (ret < 0) perror("fprintf");
      fscanf( stdin, "%d", &a );
      printf( "%d\n", a );
    
      return 0;
    }
    

    $ echo 123 | ./a.out
    -1
    fprintf: Bad file descriptor
    123
    $ 
    

    【讨论】:

      【解决方案3】:

      除了fprintf(stdin, bug 你还忘了stdin 不是键盘。最新的C11 标准不了解键盘。在 Linux 图形桌面上,只有 X11 服务器正在从物理键盘读取数据。

      实际上,在POSIX 系统上,尤其是Linux 等系统上,stdin 可以是pipe(7)(在你的shell 中使用pipelines 很常见),fifo(7)socket(7),一个普通的文件(通过redirection)甚至/dev/null,当然还有terminal

      最近有趣的是终端通常是虚拟仿真设备(我在本世纪没有看到任何真正的物理终端,除了博物馆),请阅读pseudotty。由于历史原因,细节相当神秘。阅读tty demystified 页面。另请参阅 ANSI escape code wikipage & console_codes(4)tty(4)(所以请考虑 /dev/tty 和也许 /dev/console

      您可以使用isatty(STDIN_FILENO)检查(使用isatty(3)stdin 是一个终端(实际上是一个伪终端)...

      实际上,当你真的想使用终端时,我强烈建议使用ncursesGNU readline 之类的库(两者都在使用termios(3)

      别忘了I/O一般是buffered,要明智地使用fflush(3)

      顺便说一句,您应该使用所有警告和调试信息 (gcc -Wall -Wextra -g) 进行编译,然后使用 gdb 调试器。 strace(1) 也会非常有用。

      也许你想通过管道传输到你自己的程序(但这很奇怪,而且经常是错误的,除非你非常注意所有的含义;然而,在面向事件的程序中处理signal(7) 是一个非常有用的技巧,尤其是那些有一些GUI)。请注意管道的缓冲区大小有限(因此请避免使用deadlocks,可能通过将event looppoll(2) 放在一起)并阅读有关PIPE_BUFwrite 的信息。您可能已经尝试过:

      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>
      int main() {
        int pfd[2] = {-1,-1};
        int a= 0;
        if (pipe(pfd)) { perror("pipe"); exit (EXIT_FAILURE); };
        if (dup2(pfd[0],STDIN_FILENO)<0)
            { perror("dup2 stdin"); exit(EXIT_FAILURE);};
        if (dup2(pfd[1],STDOUT_FILENO)<0) 
            { perror("dup2 stdout"); exit(EXIT_FAILURE);};
        if (printf("%d\n", 123)<=0) { perror("printf"); exit(EXIT_FAILURE); };
        if (fflush(stdout)) { perror("fflush"); exit(EXIT_FAILURE); };
        if (scanf("%d", &a)<1) { perror("scanf"); exit(EXIT_FAILURE); };
        if (a != 123) { fprintf(stderr, "impossible happened a=%d\n", a); };
        fprintf(stderr, "done...got a=%d\n", a);
      }
      

      您应该阅读Advanced Linux Programming 并了解有关syscalls(2) 的更多信息;它有几个与此相关的章节。仔细阅读pipe(2)dup2(2) 并注意上面的程序对于更大的输出是错误的(更大的PIPE_BUF,在我的系统上是几千字节)

      顺便说一句,您可以使用fmemopen(3) 从内存缓冲区中获取可读的FILE*。对于写入(例如使用fprintf)到输出缓冲区,请考虑open_memstream,并且在访问输出缓冲区之前不要忘记fflush

      【讨论】:

      • "非常感谢 Basile!我会尝试研究您在回答中写的内容。我目前的主题是二进制翻译,我相信它会对我有所帮助:)"
      【解决方案4】:

      您可以ungetc() 几个字符,然后用fscanf() 读取它们。

      #include <stdio.h>
      
      int main()
      {
          int value = 0;
      
          ungetc ( '\n', stdin);//reverse order. newline first here but last from fscanf 
          ungetc ( '3', stdin);
          ungetc ( '2', stdin);
          ungetc ( '1', stdin);
          fscanf ( stdin, "%d", &value);
          printf ( "value is %d\n", value);
      
          return 0;
      }
      

      输出:值为 123

      【讨论】:

      • 谢谢。这是一个棘手的方法,但它适用于我的实现。
      【解决方案5】:

      您认为fscanf(stdin, "format", ...); 不会阻塞并等待输入是完全错误的,因为确实如此。

      【讨论】:

        猜你喜欢
        • 2011-01-14
        • 1970-01-01
        • 2017-02-04
        • 1970-01-01
        • 2014-03-23
        • 2011-11-05
        • 1970-01-01
        • 2013-01-01
        • 2020-07-16
        相关资源
        最近更新 更多