如何让scanf读取超过4095个字符作为输入?

时间:2016-09-17 11:52:03

标签: c linux gcc

我们在c中有测试应用程序,它使用字符串格式的scanf输入和它用于进一步处理的字符串。

到目前为止,一切工作正常,但是最近我们有条件需要输入超过4100个字节而scanf需要读取它们,但是scanfstdin读取不超过4095 {1}}。 最简单形式的有问题的代码如下,

#include <stdio.h>
#include <string.h>

int main()
{
    char input_array[5000];
    int len;
    printf("Enter key: ");
    scanf("%s",input_array);
    len = strlen(input_array);

    printf("Message: %s\n",input_array);
    printf("Message Len: %d\n",len);
    return 0;
}

我认为这种情况正在发生,因为scanf最多可以读取两行,一行大小为2k。(如果我错了,请纠正我)。

现在,如果我们从文件中读取字符,此代码也可以工作,但我们还需要更改其他测试代码:(

目前,我们正在终端上复制并粘贴4200个字符,以便为scanf提供输入。

现在我的问题是,
有没有办法指示scanf读取2行以上?
还有其他我们可以使用的功能没有这个限制吗?

2 个答案:

答案 0 :(得分:4)

这是因为您的终端输入缓存在内核的I/O queue中。

  

终端设备的输入和输出队列在内核中实现一种缓冲形式,与I / O流实现的缓冲无关。

     

终端输入队列有时也称为其先行缓冲区。它包含从终端接收但尚未被任何进程读取的字符。

     

输入队列的大小由MAX_INPUT和_POSIX_MAX_INPUT参数描述;

默认情况下,您的终端位于Canonical mode

  

在规范模式下,所有输入都会保留在队列中,直到收到换行符,因此当您输入很长的行时,终端输入队列可能会填满。

现在回答你的问题:

有没有办法指示scanf阅读超过2行?

那2行概念是错误的。无论如何,如果终端的I / O队列的最大大小设置为4096字节,则不能指示scanf读取超过4096个字节。

我们可以使用哪些其他功能没有这个限制吗?

不,你甚至不能使用任何其他功能。这不是scanf的限制。

编辑:找到一种相当标准的方法

我们可以将终端的输入模式从canonical mode更改为non-canonical mode.

要更改输入模式,我们必须使用low level terminal interface

我们可以按如下方式完成任务:

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

int clear_icanon(void)
{
  struct termios settings;
  int result;
  result = tcgetattr (STDIN_FILENO, &settings);
  if (result < 0)
    {
      perror ("error in tcgetattr");
      return 0;
    }

  settings.c_lflag &= ~ICANON;

  result = tcsetattr (STDIN_FILENO, TCSANOW, &settings);
  if (result < 0)
    {
      perror ("error in tcsetattr");
      return 0;
   }
  return 1;
}

int main()
{
    clear_icanon(); // Changes the input mode of terminal from canonical mode to non canonical mode.

    char input_array[5000];
    int len;
    printf("Enter key: ");
    scanf("%s",input_array);
    len = strlen(input_array);

    printf("Message: %s\n",input_array);
    printf("Message Len: %d\n",len);
    return 0;
}

如果您想知道如何从终端

进行操作
$ stty -icanon (changes the input mode to non-canonical)
$ stty icanon (changes it back to canonical)

早期答案是 :(此技巧为older

我不知道这是否是最佳方式,但可以通过将终端模式从cooked(默认)更改为cbreak或{{1}来完成模式。

当终端处于raw模式时,它一次使用单个字符,而不是强制等待整行,然后一次性输入所有行。

在执行程序之前,您可以在终端中使用cbreak

以编程方式使用cbreak模式

首先通过运行

安装stty cbreak
curses

接下来编辑程序如下:

$ sudo apt-get install libncurses5-dev

现在使用#include <stdio.h> #include <string.h> #include <curses.h> int main() { initscr(); //start curses mode cbreak(); //change the terminal mode to cbreak. Can also use raw(); endwin(); //end curses mode char input_array[5000]; int len; printf("Enter key:"); scanf("%s",input_array); len = strlen(input_array); printf("Message:%s\n",input_array); printf("Message Len:%d\n",len); return 0; } 选项

进行编译
-lcurses

答案 1 :(得分:2)

正如ARBY所说:实际问题是LibC和终端的缓冲区之间的差异。如果你接受这个限制,你就可以了。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
  char input_array[5000];
  size_t len;
  int res;

  printf("BUFSIZ = %d\n", BUFSIZ);

  while ((res = scanf("%4095s", input_array)) == 1) {
    len = strlen(input_array);
    printf("Message Len:%zu\n", len);
  }
  return 0;
}

输出:

$ dd if=<(yes hi|tr -d '\n') bs=4200 count=2 of=longline
$ gcc-4.9 -O3 -g3  -W -Wall -Wextra  -std=c11 checkbuf.c -o checkbuf
$ ./checkbuf < longline
BUFSIZ = 8192
Message Len:4095
Message Len:4095
Message Len:106

修改 一种,不建议连接结果的方法涉及一点点指针 - 玩杂耍:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
  char input_array[10000];
  char *ptoarr;
  size_t len;
  int res;

  printf("BUFSIZ = %d\n", BUFSIZ);

  ptoarr = input_array;

  while ((res = scanf("%4095s", ptoarr)) == 1) {
    len = strlen(ptoarr);
    // TODO check that total length is smaller than or equal to input_array size
    printf("Message Len:%zu\n", len);
    ptoarr += len;
  }
  len = strlen(input_array);
  printf("Message Len:%zu\n", len);
  return 0;
}

但是,正如我所说,不推荐。