幾種從stdin讀入字符串的方式

gets函數


原型:

char * gets ( char * str );
返回值:讀入成功,返回與參數buffer相同的指針;讀入過程中遇到EOF或發生錯誤,返回NULL指針。所以在遇到返回值爲NULL的情況,要用ferror或feof函數檢查是發生錯誤還是遇到EOF。

功能:從stdin流中讀取字符串,直至接受到換行符或EOF時停止,並將讀取的結果存放在buffer指針所指向的字符數組中。換行符不作爲讀取串的內容,讀取的換行符被轉換爲'\0'空字符,並由此來結束字符串。

注意:本函數可以無限讀取,不會判斷上限,所以應該確保buffer的空間足夠大,以便在執行讀操作時不發生溢出。如果溢出,多出來的字符將被寫入到堆棧中,這就覆蓋了堆棧原先的內容,破壞一個或多個不相關變量的值。這個事實導致gets函數只適用於玩具程序,    爲了避免這種情況,我們可以用fgets(stdin) (fgets實際上可以讀取標準輸入(即大多數情況下的鍵盤輸入)來替換gets()。在V7的手冊(1979年)中說明:爲了向後兼容,gets刪除換行符,gets並不將換行符存入緩衝區。

由於可以無限讀取,ANSI標準中刪除了gets()函數,使用一個新的更安全的函數gets_s()替代(具體用法看下面示例)。
#include <stdio.h>    //這個頭文件包含gets()函數,這個函數在ISO/IEC 9899 2011(C11)標準中被移除
 
int main(void)
{
    char str1[5];  //不要char*p,然後gets(p),這是錯誤的,因爲p沒有指向有效的內存,它可能指向任何非法地址
    gets(str1);
    printf("%s\n", str1);
    return 0;
}
 
#include <stdio.h>//gets_s()用法
#define CH 20
int main(void)
{
    char ch[CH];
    printf("請輸入你的名字:\n");
    gets_s(ch,CH);             //這裏不能用gets_s(ch);
    printf("這是你的名字:%s\n", ch);
    return 0;
}
其中s爲字符串變量(字符串數組名或字符串指針)。

gets(s)函數與scanf("%s",s)相似,但不完全相同,使用scanf("%s",s) 函數輸入字符串時存在一個問題,就是如果輸入了空格會認爲字符串結束,空格後的字符將作爲下一個輸入項處理,但gets()函數將接收輸入的整個字符串直到遇到換行爲止。
也就是說:gets()函數讀取到\n(我們輸入的回車)於是停止讀取,但是它不會把\n包含到字符串裏面去。然而,和它配合使用的puts函數,卻在輸出字符串的時候自動換行。


說明:gets(s) 函數中的變量s爲一字符串指針。如果爲單個字符指針,編譯連接不會有錯誤,但運行後內存溢出錯誤。

寬字符版本,當使用unicode寬字符文本時,使用這個函數 _getws();
在C11標準中被刪除,可用C標準庫中的fgets代替.


fgets函數
       從文件結構體指針stream中讀取數據,每次讀取一行。讀取的數據保存在buf指向的字符數組中,每次最多讀取bufsize-1個字符(第bufsize個字符賦'\0'),如果文件中的該行,不足bufsize個字符,則讀完該行就結束。如若該行(包括最後一個換行符)的字符數超過bufsize-1,則fgets只返回一個不完整的行,但是,緩衝區總是以NULL字符結尾,對fgets的下一次調用會繼續讀該行。函數成功將返回buf,失敗或讀到文件結尾返回NULL。因此我們不能直接通過fgets的返回值來判斷函數是否是出錯而終止的,應該藉助feof函數或者ferror函數來判斷。


函數原型

char *fgets(char *buf, int bufsize, FILE *stream);
參數

*buf: 字符型指針,指向用來存儲所得數據的地址。
bufsize: 整型數據,指明存儲數據的大小。
*stream: 文件結構體指針,將要讀取的文件流。


返回值

1.成功,則返回第一個參數buf;
2.在讀字符時遇到eof,則eof指示器被設置,如果還沒讀入任何字符就遇到這種情況,則buf保持原來的內容,返回NULL;
3.如果發生讀入錯誤,error指示器被設置,返回NULL,buf的值可能被改變。


注意1:《UNIX 環境高級編程》中指出,每次調用fgets函數會造成標準輸出設備自動刷清!案例詳見《UNIX環境高級編程(第二版)》中程序清單1-5和課後習題5.7,習題5.7的答案中給出了相關的論述。

注意2:初入門者,大多數是在WINDOWS下,使用VS進行練習的。此環境下,對注意1中的情況進行測試,並不能看到案例中所描述的情景,因爲具體的實現不同。
stream文件流指針體指向文件內容地址的偏移原則
如果使用fgets()讀取某個文件,第一次讀取的bufsize爲5,而文件的第一行有10個字符(算上'\n'),那麼讀取文件的指針會偏移至當前讀取完的這個字符之後的位置。也就是第二次再用fgets()讀取文件的時候,則會繼續讀取其後的字符。而如果使用fgets() 讀取文件的時候bufsize大於該行的字符總數加2(多出來的兩個,一個保存文件本身的'\n'換行,一個保存字符串本身的結束標識'\0'),文件並不會繼續讀下去,僅僅只是這一行讀取完,隨後指向文件的指針會自動偏移至下一行。
例:
如果一個文件的當前位置的文本如下
Love, I Have
Since you can do it.
如果用fgets(str1,6,file1);去讀取
則執行後str1 = "Love," ,讀取了6-1=5個字符
這個時候再執行fgets(str1,20,file1)則執行後str1 = " I Have\n"
而如果
fgets(str1,23,file1);
則執行str1="Love ,I Have",讀取了一行(包括行尾的'\n',並自動加上字符串結束符'\0'),當前文件位置移至下一行,雖然23大於當前行上字符總和,可是不會繼續到下一行。而下一次調用fgets()繼續讀取的時候是從下一行開始讀。
#include<string.h>
 
#include<stdio.h>
 int main ( void )
{
    FILE*stream;
    char string[]="Thisisatest";
    char msg[20];
    stream=fopen("DUMMY.FIL","w+");
    fwrite(string,strlen(string),1,stream);
    fseek(stream,0,SEEK_SET);
    fgets(msg,strlen(string)+1,stream);
    printf("%s",msg);
    fclose(stream);
    return 0;
}

fgets函數用來從文件中讀入字符串。fgets函數的調用形式如下:fgets(str,n,fp);此處,fp是文件指針;str是存放在字符串的起始地址;n是一個int類型變量。函數的功能是從fp所指文件中讀入n-1個字符放入str爲起始地址的空間內;如果在未讀滿n-1個字符之時,已讀到一個換行符或一個EOF(文件結束標誌),則結束本次讀操作,讀入的字符串中最後包含讀到的換行符。因此,確切地說,調用fgets函數時,最多隻能讀入n-1個字符。讀入結束後,系統將自動在最後加'\0',並以str作爲函數值返回。

同時可以用作鍵盤輸入:fgets(key,n,stdin)且還必須:key[strlen(key)]='\0'或者key[n-1]='\0'
還有種程序經常使用的方法:key[strlen(key-1)]=0x00;
與gets相比使用這個好處是:讀取指定大小的數據,避免gets函數從stdin接收字符串而不檢查它所複製的緩存的容積導致的緩存溢出問題。

getline函數


getline不是C庫函數,而是C++庫函數。它會生成一個包含一串從輸入流讀入的字符的字符串,直到以下情況發生會導致生成的此字符串結束。1)到文件結束,2)遇到函數的定界符,3)輸入達到最大限度。

說明:

在函數遇到和結束定界符相等的字符時函數結束,同時函數抽出定界符,此種情況下該定界符既不被放回輸入流,也不被放入要生成的字符串。所以由此可以理解輸入結束後的第一個回車是定界符,被確認後拋棄,而第二個纔是程序執行運行時正常需要的!
用於讀取一行字符直到換行符,包括換行符[1].


函數原型:

ssize_t getline(char **lineptr, size_t *n, FILE *stream);
返回值

成功:返回讀取的字節數。
失敗:返回-1。


參數:

lineptr:指向存放該行字符的指針,如果是NULL,則有系統幫助malloc,請在使用完成後free釋放。
n:如果是由系統malloc的指針,請填0
stream:文件描述符


應用舉例

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
  FILE * fp;
  char * line = NULL;
  size_t len = 0;
  ssize_t read;
       fp = fopen("/etc/motd", "r");
  if (fp == NULL)
  exit(EXIT_FAILURE);
       while ((read = getline(&line, &len, fp)) != -1)
       {
      printf("Retrieved line of length %zu :\n", read);
      printf("%s", line);
  }
       if (line)
      free(line);
  exit(EXIT_SUCCESS);
}

————————————————
版權聲明:本文爲CSDN博主「博弈Dream」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/GUI1259802368/article/details/78073415

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章