我们在c中有测试应用程序,它使用字符串格式的scanf
输入和它用于进一步处理的字符串。
到目前为止,一切工作正常,但是最近我们有条件需要输入超过4100个字节而scanf
需要读取它们,但是scanf
从stdin
读取不超过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行以上?
还有其他我们可以使用的功能没有这个限制吗?
答案 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;
}
但是,正如我所说,不推荐。