C/C++中的輸入與輸出及如何讀取一行文本


在使用C/C++進行編程的過程中,經常會遇到輸入輸出的問題。

對於C語言中,

1. 格式化輸入輸出。

在C語言中,最常用的格式化輸入輸出是scanf和printf函數。

和這兩個函數對應的更安全的函數是fscanf和fprintf:指定文件指針

對於字符串的處理還有sscanf和sprintf:指定字符串

聲明如下:

int printf(const char *format, ...);

int fprintf(FILE *stream, const char *format, ...);

int sprintf(char *str, const char *format, ...);

int snprintf(char *str, size_t size, const char *format, ...);

 

int scanf(const char *format, ...);

int fscanf(FILE *stream, const char *format, ...);

int sscanf(const char *str, const char *format, ...);

 

2. 字符輸入輸出

出了這些格式化輸入輸出函數之外,還有很多針對字符輸入輸出的函數:

包括getc,getchar,gets,fgetc,fgets,putc,putchar,puts,fputc,fputs。

首先單個字符輸入/輸出函數對應的聲明如下:

int fgetc(FILE *stream); int getc(FILE *stream);

從文件中讀入一個字符,返回值就是讀入的字符,如果錯誤返回EOF。

這兩個函數具有相同的效果,但是getc是宏,而fgetc是函數。程序員常用這個宏,因為它比調用函數更快。

int fputc(int c,FILE*stream); int putc(int c,FILE*stream);

輸出字符到文件。返回輸出的字符(轉化為int型),如果錯誤返回EOF。

putc也是宏實現的。

int getchar(void),int putchar(int c)

這兩個也是宏實現,主要是從標准輸入輸出讀取或輸出字符。等同於getc(stdin)和putc(c,stdout)

其次是多個字符/字符串的輸入/輸出函數對應的聲明:

char *fgets(char *s, int size, FILE *stream);char *gets(char *s);

讀入字符串,fgets適合取代gets,因為gets無法限制讀取字符的個數。gets是直接從標准輸入讀取。

fgets從流中讀入最多n-1個字符,最后加一個空字符作為字符串結尾標記。如果在讀到最大個數的字符之前遇到了一個換行字符或者文件結尾,那么只有目前所讀入的字符會被放入到緩沖區中,如果讀到換行符'\n',那么此字符也會被放入到緩沖區中。

返回值為讀入的字符串,如果出錯,返回值為NULL

int fputs(const char *s,FILE*stream); int puts(const char*s);

返回輸出的字符的個數,如果出錯,返回EOF。

需要注意的是fgets保留換行符'\n',而gets是從stdin輸入,在讀取字符串時會刪除結尾的換行符'\n';

同樣,fputs寫入時不包括換行符,而puts在寫入字符串時會在末尾添加一個換行符。

 

3. 二進制輸入輸出

對於二進制的輸入輸出,主要采用fread和fwrite函數,這個比較單一。

聲明如下:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

其中ptr是緩沖區指針,size是讀取的元素的個數,nmemb是元素的大小(sizeof)

返回值是正確讀寫的字節數,如果出錯或達到文件末尾eof,返回一個小的值或0

必須自己判斷文件結尾還是出錯,分別使用feof函數和ferror函數

 

對於C++語言,

采用的是輸入輸出流來進行的。由於C++是面向對象語言,所以C++中采用的是流類。

下圖是C++的一個類繼承方式:

可以看到cin,cout,ifstream,ofstream,istringstream,ostringstream是對應的輸入輸出類。而fstream和stringstream類是可以同時進行輸入和輸出。

 

1. cin,ifstream和istringstream都是從istream繼承而來,所以,我們可以分析一下istream類的成員函數:

對於istream,可以分為格式化輸入和非格式化輸入兩種形式:

(1)格式化輸入

采用的時候對操作符>>的重載。並且操作符>>在對待輸入上遇到空格就停止。

(2)非格式化輸入

分為對於字符/字符串的輸入和對於字節的輸入,包括的函數主要有get,getline,read,readsome,peek等。

其中get是獲取一個字符,getline是獲取一行字符。read和readsome是讀入字節。

get函數的聲明如下:

int get();

istream& get ( char& c );

istream& get ( char* s, streamsize n );

istream& get ( char* s, streamsize n, char delim );

istream& get ( streambuf& sb);

istream& get ( streambuf& sb, char delim );

可以從輸入設備獲得一個字符,也可以讀取字符串。默認采用'\n'作為分隔符。

使用get函數時,get函數與getline接受的參數相同,解釋參數的方式也相同,並且都讀取到行尾,但是get不再讀取並丟棄換行符,而是將其留在輸入隊列中。所以經常出問題。由於第一次調用后,換行符留在隊列中,因此第二次調用時看到第一個字符邊是換行符,因此get認為已經到達隊尾,而沒有發現任何可讀取的內容。如果不借助於幫助,get將不能跨過該換行符。可以采用get的另一種重載的形式來讀取這個換行符,然后為下一行的輸入做好准備。

但是對於空行get經常出問題。當get讀取空行后將設置失效位(failbit),這意味着接下來的輸入將被阻斷,但可以用下面的命令來恢復輸入。

cin.clear();

 

getline函數的聲明如下:

istream& getline (char* s, streamsize n );

istream& getline (char* s, streamsize n, char delim );

從輸入讀入字符串到s中,n是緩沖區最大容量。默認采用'\n'作為分隔符。

 

read和readsome函數的聲明如下:

istream& read ( char* s, streamsize n );

streamsize readsome ( char* s, streamsize n );

這兩個函數不是很熟悉。之前很少遇到過。

read()方法從緩沖區或設備讀取指定長度的字節數,返回對自身的引用.

而readsome()方法只能從緩沖區中讀取指定長度字節數,並返回實際已讀取的字節數.

比如:

const int LEN = 20;

char chars[ LEN + 1 ] = {0};

ifstream in( fileName );

in.read( chars, LEN );//將文件從設備載入緩沖區,並讀取LEN長度.

cout << chars << endl;

in.readsome( chars, LEN );//就可以從緩沖區中讀取. 在緩沖區中沒有數據時,用readsome()得不到任何數據.

cout << chars << endl;

 

而有時候想要從設備讀取指定長度的數據,但又要知道實際讀取的長度,這時候就要用另一個方法: gcount()

它返回自上次讀取以來所讀取的字節數,因此可以這樣得到實際讀取的長度.

 

int count = 0;

in.read( chars, LEN );

count = in.gcount();

cout << "Read in " << count << " chars : " << chars << endl;

 

實際上,readsome()也是調用read()和gcount()來實現的.

 

C++為了采用string類而引入了一個全局的輸入函數getline,其參數是string類型的:

istream& getline ( istream& is, string& str, char delim );

istream& getline ( istream& is, string& str );

這個函數還是非常有用的。為什么會出現這個getline函數呢?原因:cin是istream的一個對象,cin.getline()中的getline()也就是istream的一個成員函數,在string被加入到c++很久以前,istream就已經存在了,所以,參數針對的是數組類型的存儲,而沒有string。

 

2. cout,ofstream和ostringstream都是從ostream繼承而來,所以,我們可以分析一下ostream類的成員函數:

ostream也是分為格式化和非格式化輸出,與istream對應的。

(1)格式化輸出

采用對操作符<<重載的方式。

(2)非格式化輸出

主要包括put函數和write函數兩個。put函數輸出一個字符,write函數輸出字節。

相比輸入istream,輸出ostream中的相關函數少了很多,其實主要是因為輸出相比輸入更簡單,輸出操作符<<可以解決到大部分情況。

put函數

ostream& put ( char c );

write函數

ostream& write ( const char* s , streamsize n );

 

3. 在使用這些函數的時候,一個主要的問題是如何判斷輸入是否完成,即循環的控制條件該怎么寫?

C++中是通過判斷輸入流的狀態來得到輸入是否完成或出錯的。

下面的就是幾個相關的函數:good,eof,fail,bad

(1) good函數

bool good ( ) const;

檢查輸入流是否良好,可否繼續進行輸入。主要檢查流的3個狀態標志:eofbit, failbit 和badbit

(2) eof函數

bool eof ( ) const;

檢查eofbit標志。用於測試是否達到文件末尾(EOF)

(3) fail函數

bool fail ( ) const;

檢查failbit和badbit。測試是否出現輸入錯誤。

(4) bad函數

bool bad ( ) const;

檢查badbit。檢查是否出錯。

所以在具體的使用中,可以使用good函數來作為循環的判斷條件。

 

所以,我們總結一下在C/C++中讀取一個字符或字符串可以采用的方式。

1. 首先,讀取一個字符:

C語言方式:

(1).采用getchar從stdin輸入

while((c=getchar())!=EOF)

putchar(c);

(2). 采用fgetc/getc輸入

while((c=fgetc(stdin))!=EOF)

fputc(c,stdout);

while((c=getc(stdin))!=EOF)

putc(c,stdout);

C++語言方式:

(1).采用重載操作符>>

while(cin>>c)

cout<<c<<endl;

這種形式會跳過所有的空白符,包括空格,換行,制表符

(2). 采用get函數

char c;

while(cin.good())

{

c=cin.get();

if(cin.good())//這兒就是判斷是否讀入了有效的字符

cout<<c;

}

這兒可以讀入任何字符。

或者

char c;

while((c=cin.get())!=-1)

cout<<c;

這兒就是直接判斷讀到的字符,類似C中的思想。

 

2. 其次, 讀取一行文本:

C語言方式:

(1). 采用gets函數,從stdin輸入

char str[256];

while(gets(str)!=NULL)

puts(str);

(2). 使用比較安全的fgets函數

char str[256];

while(fgets(str,256,stdin)!=NULL)

fputs(str,stdout);

(3). gcc中擴展的函數

int read;

int len=0;

char *line=NULL;

while((read=getline(&line,&len,stdin))!=-1)

printf("%s\n",line);

free(line);

 

C++語言方式:

(1).采用操作符重載>>

string s;

while(cin>>s)

cout<<s<<endl;

這種方式會以空白(空格,換行,制表)為分隔符,不斷的讀入字符串。

(2). 采用istream類的成員函數getline

char str[256];

while(cin.good()){

cin.getline(str,256);

cout<<str<<endl;

}

char str[256];

while(cin.getline(str,256)){

cout<<str<<endl;

}

(3). 采用全局函數getline

string str;

while(getline(cin,str)){

cout<<str<<endl;

}

 

(4). 采用get函數

char str[265];

while(cin.good()){

cin.get(str,256);

cin.get();

cout<<str<<endl;

}

 

總結來看,如果要讀取一行的話,

對於C語言,可以采用fgets函數,或者如果使用linux平台的話,可以采用擴展的getline函數。

注意,這兩個函數都是要讀入最后的換行符的。

對於C++語言,如果使用C字符串的話,就采用cin.getline()函數,如果采用string型字符串的話,就采用全局函數getline(cin,n);

注意,這兩個函數都不讀入最后的換行符。

 

這兒有一個關於getline函數的簡單的總結:http://www.cnblogs.com/xkfz007/archive/2012/02/27/2363810.html

這兒有一個關於cin中的getline和get函數的比較:http://www.cnblogs.com/xkfz007/archive/2012/04/06/2435251.html

這兒是對C中輸入輸出函數的一個簡單總結:http://www.cnblogs.com/xkfz007/archive/2012/02/27/2363810.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM