2016-09-17 195 views
4

我们在c中有测试应用程序,它使用字符串格式的scanf和它用于进一步处理的字符串进行输入。如何使scanf读取超过4095个字符作为输入?

到目前为止,一切工作正常,但是最近我们有状态,其中需要投入超过4100个字节,scanf需要看他们不过scanf不从stdin读超过4095。 最简单的有问题的代码形式如下,

#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

'scanf'停在第一个空白处,''\ n'',''\ t''和''''被认为是空格,您可以使用'fgets'并去掉尾随换行符。 –

+1

C11草案标准n1570:* 7.21.6.2 fscanf函数12 转换说明符及其含义如下:[...] s 匹配一系列非空白字符[...] * – EOF

+0

@AlterMann我们从字符串中删除了所有新行。我使用dd来生成大字符串。例如'dd if = <(yes hi | tr -d'\ n')bs = 4100 count = 1' – AnkurTank

回答

3

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

终端设备的输入和输出队列在内核中执行缓冲形式,而不依赖于由I/O流实现的缓冲。

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

输入队列的大小由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

我不知道whe这是最好的方式,但它可以通过将终端模式从cooked(默认)更改为cbreakraw模式来完成。

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

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

要使用CBREAK模式编程

首先通过运行

$ sudo apt-get install libncurses5-dev 

下一页编辑程序安装curses包如下:

#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选项

$ gcc 1.c -lcurses 
+0

非常感谢ARBY,只是很小的问题,我们是否必须重新规范标志? – AnkurTank

+0

@AnkurTank倒退更安全。 – Raman

+0

哎呀...现在接受了。我认为它确实解决了这个问题。 – AnkurTank

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; 
} 

但是,正如我所说的, 不建议。

+0

我们如何打破while循环?对我来说,它的无限while循环:( – AnkurTank

+0

@AnkurTank你是否正确地使用了我给你的代码,使用'dd'编译输入文件的结构,编译器阶段(好吧,不应该和其中之一当前版本的GCC)和调用?你的输出是什么? – deamentiaemundi

+0

ohh我没有使用编译命令,你给了我,让我试试看。 – AnkurTank

相关问题