2016-09-17 163 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

相關問題