要點3:輸入函數對比與自定義輸入方式

讀取輸入的方式

相關函數原型(從控制檯獲取輸入,不考慮寬字符):數組

int scanf( const char *format, ... );
int getchar(void);
char *gets( char *str );
char *gets_s( char *str, rsize_t n );
char *fgets( char *str, int count, FILE *stream );
  • scanf
    • 若是解析錯誤,內容繼續留在緩衝區供下次使用;
    • 解析失敗返回0,成功返回解析的參數個數,不會超過佔位符個數,讀到文件尾返回EOF(-1);
    • 讀取字符串,一次只能讀取一個詞,不能用scanf讀取一行;
  • getchar
    • 能夠讀取到換行符;
    • 經常使用於暫停程序,或丟棄緩衝區剩餘字符;
  • gets
    • 讀取一行,遇到換行符,直接丟棄換行符;
    • 會自動在字符串末尾添加\0
    • 返回字符串指針,讀取失敗返回null;
  • gets_s
    • 能夠設置讀取的字符串長度;
    • 讀到換行符,將換行符丟棄;
    • 若是讀取到最大字符數,尚未讀取到換行符或文件結尾,讀取並丟棄隨後的輸入直至遇到換行符或EOF;
    • 返回字符串指針,讀取失敗返回null;
    • c11的可選函數
  • fgets
    • 能夠設置讀取的字符串長度;
    • 讀到換行符不丟棄存到數組裏;
    • 不會自動清除行緩衝區剩餘數據;
    • 返回字符串指針,讀取失敗返回null;

scanf

該函數能夠從標準輸入讀取內容,返回值爲讀取的參數個數,例如:函數

#include <stdio.h>

int main()
{
    int seed;
    printf("%d\n", scanf("%d %d", &seed, &seed));
}

運行程序,輸入 兩個整數,打印爲 2,測試輸入3個值仍然打印2,是由於這個"%d %d"指定了只解析兩個int,多餘的將留在緩衝區中,若是後面再寫一個scanf,將從緩衝區中繼續解析。測試

如今多加一個scanf指針

#include <stdio.h>

int main()
{
    int seed;
    printf("%d\n", scanf("%d %d", &seed, &seed));
    printf("%d\n", scanf("%d %d", &seed, &seed));
}

case1:讀取到文件尾部返回EOF

1

輸出:code

1
-1

scanf從緩衝區中解析,返回解析成功的參數個數,由於只有一個1,因此第一個給scanf解析,第一行打印1,第二行解析的時候因讀取到了文件結束表示EOF返回-1。orm

case2:解析失敗返回0

f

輸出:字符串

0
0

這說明,解析失敗的內容還留在緩衝區給下次scanf用,因此兩個scanf都返回的0。get

由於沒法解析的值會繼續留在緩衝區供下次使用,因此若是是循環scanf,程序就會跑飛,讓你沒有輸入的機會,可使用綜上一節提供的示例測試一下,運行後直接輸入f原型

case3:返回值最大爲佔位符個數

1 2 3 4 5 6 7

輸出:string

2
2

這代表,返回值最大是佔位符的個數,剩下的內容還留在緩衝區。

綜上

scanf判斷輸入結束,只能在文件輸入模式下利用EOF判斷,例如:

// qwer.c
#include <stdio.h>

int main()
{
    int a;
    while(scanf("%d", &a) != EOF)
    {
        printf("%d\n", a);
    }
    return 0;
}

輸入文件test.txt內容:

1
2
3
4

編譯: gcc qwer.c -o main -std=c11
運行:./main < test.txt

getchar

這個函數能夠從輸入緩衝區僅讀取一個字符,返回int,後面結合fgets使用。

gets

在讀取字符串時,scanf()和轉換說明%s只能讀取一個單詞,但是程序中常常要讀取一整行輸入。gets函數簡單易用,它讀取整行輸入,直到遇到換行符,而後丟棄換行符,存儲其他字符,並在這些字符的末尾添加一個空字符使其成爲一個c字符串。它常常和puts函數配對使用,該函數用於顯示字符串,並在末尾添加換行符。

#include <stdio.h>
#define STLEN 81
int main()
{
    char words[STLEN];
    puts("Enter a string, please.");
    gets(words);   // 典型用法
    printf("Your string twice:\n");
    printf("%s\n", words);
    puts(words);
    puts("Done.");
    return 0;
}

printf("%s\n", words);puts(words);效果相同,可是編譯的時候會產生警告,由於gets讀取整行輸入,並不知道words能存多少,若是輸入字符串過長,會致使緩衝區溢出。

例如將STLEN設置成5,程序依然能夠運行,嘗試輸入過長的數據就可能會發成溢出,最直觀的就是能夠看到發生段溢出後程序異常退出。

gets_s

該函數是c11纔有的,且爲拓展函數,使用方式除了能夠設置讀取的字符數以外和gets函數用法同樣。

fgets

這個函數除了能夠從標準輸入讀取字符串以外,還能夠從文件中讀取,並且能夠指定讀取字符個數,比gets_s更加靈活易用,利用fgets,可是fgets不會自動丟棄超過字符個數以外的行緩衝區數據,因此要配合getchar將剩餘的緩衝數據丟棄,不然可能形成程序運行以異常。

s_gets【自定義輸入】

爲知足如下幾點編寫自定義輸入:

  • 從標準輸入讀取數據;
  • 可以指定讀取字符個數;
  • 丟棄換行符;
  • 丟棄行緩衝區剩餘數據;
char *s_gets(char *str, int n)
{
    char *ret_var;
    int i = 0;
    ret_var = fgets(str, n, stdin);
    if(ret_var)
    {
        while(str[i] != '\n' && str[i] != '\0')
        {
            i++;
        }
        if(str[i] == '\n')
            str[i] = '\0';
        else
            while(getchar() != '\n')
                continue;
    }
    return ret_var;
}

使用方式

#include <stdio.h>
#define STLEN 8

int main()
{
    char words[STLEN];
    while(s_gets(words, STLEN) && words[0] != '\n') // 沒有輸入數據會自動退出程序
    {
        puts(words);
    }
    return 0;
}
相關文章
相關標籤/搜索