getline函数
在我的印象中,getline函數常常出如今自己的視野里,模糊地記得它常常常使用來讀取字符串
。可是又對它的參數不是非常了解,今天又用到了getline函數,如今來細細地總結一下:
首先要明確設計getline函數的目的,事實上非常easy,就是從流中讀取字符串。并且讀取的方
式有非常多,包含依據限定符,依據已讀取的字符的個數。從這個函數的名稱來看,它的直觀
意義是從流中讀取一行,可是大家不要被這表面的現象所迷惑。事實上假設讓我來為這個函數
去一個名字的話,也許我會取一個getString,由于它的目的本來就是從流中讀取字符的序
列,而不是像get函數那樣一次讀取一個字符。
另外要注意,C++中有兩個getline函數,一個是在string頭文件里,定義的是一個全局的
函數,函數聲明是istream& getline ( istream& is, string& str, char delim )與 istream& getline ( istream& is, string& str );還有一個則是istream的成員函數,函
數聲明是istream& getline (char* s, streamsize n )與istream& getline (char*?
s, streamsize n, char delim );注意第二個getline是將讀取的字符串存儲在char數組
中而不能夠將該參數聲明為string類型,由于C++編譯器無法運行此默認轉換。
以下依據一個樣例簡單地介紹一下該函數: test.txt文件例如以下所看到的:
abcd efgh ijk
如今先嘗試全局函數getline。從函數聲明中我們觀察到兩種函數聲明的不同主要體如今參
數的個數上,假設是兩個參數的話,那么默認的限定符便是‘\n’了,可是假設聲明了限
定符,'\n'是否仍然有效呢?我寫了例如以下程序做測試:
int main(){ int n = 6; string tem; ifstream infile("test.txt"); for(int i = 0;i<n;i++){ //getline(infile,tem); getline(infile,tem,'\t'); cout<<tem; } return 0; }
輸出結果是: abcd efg
從中能夠看出換行符確實失效了。所以getline函數的限定符僅僅有一個,是相互覆蓋的。
再來看一下istream的getline函數:
int main(){ char a[3]; ifstream infile("test.txt"); infile.getline(a,3,'c'); cout<<a; }
輸出結果是a 事實上istream的getline是在全局函數的getline函數的基礎上,又多了一個終止讀取的條
件,即依據已讀取的字符的個數來判定,實際上是讀取n-1個字符,由于最后要為‘\0’留
下一個位置。其它地方二者基本同樣。
原理想必也非常easy。每一次getline,文件指針都不斷向下走,相當于不斷的調用get函數
而且將已經讀取的字符保存下來。當遇到限定符或者已讀取的字符個數達到了參數的要求(
或者是因為文件的原因),那么便終止讀取。假設是碰到了限定符,那么該字符便會被?
extracted and discarded,也就是文件指針向下再移一位,可是并不保存該字符,也就
是每次getline之后,文件指針會停留在限定符的后面(遇到限定符的情況)。
可是看以下的這個情況:
int main(){ int n = 13; string tem; ifstream infile("test.txt"); for(int i = 0;i<n;i++){ //getline(infile,tem); getline(infile,tem,'\t'); cout<<tem<<endl; } return 0; }
依照我的理解的話,那么文件里總共11個字母,當文件指針停在‘\t’之后,k之前的時候
,剛好是第八次,第九次getline的時候,因為在讀過k之后,遇到了文件結束符,所以get
指針應該停留在k之后,這個時候再getline的話應該是無效的,可是輸出結果跟我想的不
一樣:
a b c d e f g h i j k k k k k
這說明第九次getline之后,get指針所指向的位置并沒有改變,這說明我想的思路有問題
,于是我在網上看了getline函數的源代碼,當中有一篇凝視比較好的:
_Myt& getline(_Elem *_Str, streamsize _Count, _Elem _Delim) ?? {// get up to _Count characters into NTCS, discard _Delim ?? _DEBUG_POINTER(_Str); ? ?//推斷傳入指針的合法性 ? ios_base::iostate _State = ios_base::goodbit; ? ? _Chcount = 0; //從輸入流中讀取的字符數 ? const sentry _Ok(*this, true); ? /*注:上面這句非常關鍵,它關系到以下的if是否運行,也就是是否讀輸入流。這句從
語法上看,是? sentry是一個class, _Ok是sentry類的一個const對象,構造這個對象時須要傳入兩個
參數? 第一個是流對象自身的引用,第二個表示對空白字符(如空格、制表符)的處理方式
,為true時意味著不忽略空白字符,即一個字符一個字符的從輸入流中提取。? */ ? if (_Ok && 0 < _Count) ?? /*?
**************************************************************************? * sentry類內部重載了一個類型轉換運算符,它把sentry類的實例轉換成了一個bool
表達式。? * 這個表達式返回sentry類的私有成員_Ok的值。? bool sentry::operator bool() const? * { // test if _Ipfx succeeded? * ? ? ? return (_Ok);? * ? }? * _Ok這個成員的值由sentry類的構造函數? * 在初始化時設置,設置的過程比較麻煩,這里不做贅述(事實上我也沒看十分明確)。? * 但能夠肯定的是,當輸入流的狀態是正常時,這個成員的值也是true,? * 反之,則是false。 ? * ? * _Count是調用者傳入的第二個參數,這里用做循環計數器的初值,以后每讀一個字
符,? * _Count的值會減一。?
****************************************************************************
**/ ? { ? // state okay, use facet to extract ?? int_type _Metadelim = _Traits::to_int_type(_Delim); ?? int_type _Meta = _Myios::rdbuf()->sgetc();//從輸入流讀一個字符 ?? for (; ; _Meta = _Myios::rdbuf()->snextc()) //snextc()從輸入流中讀取下一
個字符 ? if (_Traits::eq_int_type(_Traits::eof(), _Meta)) ?? {// end of file, quit ?? _State |= ios_base::eofbit; ?? break; ?? }//注:遇到文件尾,getline結束 ?? else if (_Meta == _Metadelim) { ? // got a delimiter, discard it and quit ?? ++_Chcount; ? ?//讀取字符數+1 ? _Myios::rdbuf()->sbumpc(); ? /*注:上面這句把結束符讀掉了,假設不指定結束符,那就是把'\n'讀掉了
。 ? 但回車符本身并沒有復制到緩沖區中,? 這樣下次的讀操作將從回車符后面的第一個字符開始,? */ ? break; ?? }/* 注:遇到結束符,getline結束,注意這里的順序,它是先推斷是否遇到結束
符,后推斷是否讀入了指定個數的。 */ ? else if (--_Count <= 0) ?? {// buffer full, quit ?? _State |= ios_base::failbit; ?? break; ?? } ? //注:讀到了指定個數,運行到這里已經隱含了在指定個數的最后一位仍然不是
結束符, ? //因此該部分將輸入流狀態置為了錯誤。 ? //這直接導致了接下來的getline(或者get)以及>>運算符等讀操作都不能正確執
行) ?? else { ? // got a character, add it to string ?? ++_Chcount; ?//讀取字符數加1 ? *_Str++ = _Traits::to_char_type(_Meta); ?? }//注:這一分支將讀取到的單個字符復制到緩沖區中 ? } ?? *_Str = _Elem(); ?// ? /* add terminating null character /*注:前面這句為字符串增加了終止符'\0'? 由于_Elem()構造了一個ascii碼為0的字符對象*/ ? _Myios::setstate(_Chcount == 0 ? _State | ios_base::failbit : _State); ? /*注:假設沒有讀入不論什么字符,要保持運行這一次getline之前的輸入流狀態,? 否則依據這一次getline運行的情況,設置輸入流為對應狀態。 */ ? return (*this); ? //返回輸入流對象本身 ? } ??
可是我認為這當中還是有問題,由于: sbumpc: advances the get pointer and returns the character pointed by it?
before the call. snextc: advances the get pointer and returns the character pointed by it?
after the call. 因為是傳引用,所以不論調用哪個,都會改變原文件流中get的指針所指向的位置。并且,
告訴大家一個更為驚奇的結果便是: 以下程序: int main(){ int n = 6; string tem; ifstream infile("test.txt"); for(int i = 0;i<n;i++){ getline(infile,tem); //getline(infile,tem,'\t'); cout<<tem<<endl; } return 0; } 的輸出結果為: abcd efgh ijk ijk ijk ijk
無論依照我的想法還是依照對上面源代碼的理解,結果都不應該是這個樣子。是源代碼錯了,還
是我的理解有問題?希望知道的朋友能指導一下。
========================================================================== 好吧,可能是編譯器的問題,用比的編譯器編譯執行了一下,結果和我的想法是一致的,跟源代碼所要表達的也是一致的 ,所以我原先的想法是沒錯的,結貼啦~ 所以假設你不斷的從文件流中getline的話,假設你想推斷是否已經達到文件結尾的話,那么僅僅需推斷getline所得到的字符串是否為 空就ok了~ 再補充一下,因為getline函數將istream參數作為返回值,和輸入操作符一樣也把它作為推斷條件。所以假設到達文件結尾的話,那么返回的文件流包括的字符為空,這個false是等價的 ,所以我們也能夠用while(getline(infile,str))來對文件流是否達到結尾進行判定。
。可是又對它的參數不是非常了解,今天又用到了getline函數,如今來細細地總結一下:
首先要明確設計getline函數的目的,事實上非常easy,就是從流中讀取字符串。并且讀取的方
式有非常多,包含依據限定符,依據已讀取的字符的個數。從這個函數的名稱來看,它的直觀
意義是從流中讀取一行,可是大家不要被這表面的現象所迷惑。事實上假設讓我來為這個函數
去一個名字的話,也許我會取一個getString,由于它的目的本來就是從流中讀取字符的序
列,而不是像get函數那樣一次讀取一個字符。
另外要注意,C++中有兩個getline函數,一個是在string頭文件里,定義的是一個全局的
函數,函數聲明是istream& getline ( istream& is, string& str, char delim )與 istream& getline ( istream& is, string& str );還有一個則是istream的成員函數,函
數聲明是istream& getline (char* s, streamsize n )與istream& getline (char*?
s, streamsize n, char delim );注意第二個getline是將讀取的字符串存儲在char數組
中而不能夠將該參數聲明為string類型,由于C++編譯器無法運行此默認轉換。
以下依據一個樣例簡單地介紹一下該函數: test.txt文件例如以下所看到的:
abcd efgh ijk
如今先嘗試全局函數getline。從函數聲明中我們觀察到兩種函數聲明的不同主要體如今參
數的個數上,假設是兩個參數的話,那么默認的限定符便是‘\n’了,可是假設聲明了限
定符,'\n'是否仍然有效呢?我寫了例如以下程序做測試:
int main(){ int n = 6; string tem; ifstream infile("test.txt"); for(int i = 0;i<n;i++){ //getline(infile,tem); getline(infile,tem,'\t'); cout<<tem; } return 0; }
輸出結果是: abcd efg
從中能夠看出換行符確實失效了。所以getline函數的限定符僅僅有一個,是相互覆蓋的。
再來看一下istream的getline函數:
int main(){ char a[3]; ifstream infile("test.txt"); infile.getline(a,3,'c'); cout<<a; }
輸出結果是a 事實上istream的getline是在全局函數的getline函數的基礎上,又多了一個終止讀取的條
件,即依據已讀取的字符的個數來判定,實際上是讀取n-1個字符,由于最后要為‘\0’留
下一個位置。其它地方二者基本同樣。
原理想必也非常easy。每一次getline,文件指針都不斷向下走,相當于不斷的調用get函數
而且將已經讀取的字符保存下來。當遇到限定符或者已讀取的字符個數達到了參數的要求(
或者是因為文件的原因),那么便終止讀取。假設是碰到了限定符,那么該字符便會被?
extracted and discarded,也就是文件指針向下再移一位,可是并不保存該字符,也就
是每次getline之后,文件指針會停留在限定符的后面(遇到限定符的情況)。
可是看以下的這個情況:
int main(){ int n = 13; string tem; ifstream infile("test.txt"); for(int i = 0;i<n;i++){ //getline(infile,tem); getline(infile,tem,'\t'); cout<<tem<<endl; } return 0; }
依照我的理解的話,那么文件里總共11個字母,當文件指針停在‘\t’之后,k之前的時候
,剛好是第八次,第九次getline的時候,因為在讀過k之后,遇到了文件結束符,所以get
指針應該停留在k之后,這個時候再getline的話應該是無效的,可是輸出結果跟我想的不
一樣:
a b c d e f g h i j k k k k k
這說明第九次getline之后,get指針所指向的位置并沒有改變,這說明我想的思路有問題
,于是我在網上看了getline函數的源代碼,當中有一篇凝視比較好的:
_Myt& getline(_Elem *_Str, streamsize _Count, _Elem _Delim) ?? {// get up to _Count characters into NTCS, discard _Delim ?? _DEBUG_POINTER(_Str); ? ?//推斷傳入指針的合法性 ? ios_base::iostate _State = ios_base::goodbit; ? ? _Chcount = 0; //從輸入流中讀取的字符數 ? const sentry _Ok(*this, true); ? /*注:上面這句非常關鍵,它關系到以下的if是否運行,也就是是否讀輸入流。這句從
語法上看,是? sentry是一個class, _Ok是sentry類的一個const對象,構造這個對象時須要傳入兩個
參數? 第一個是流對象自身的引用,第二個表示對空白字符(如空格、制表符)的處理方式
,為true時意味著不忽略空白字符,即一個字符一個字符的從輸入流中提取。? */ ? if (_Ok && 0 < _Count) ?? /*?
**************************************************************************? * sentry類內部重載了一個類型轉換運算符,它把sentry類的實例轉換成了一個bool
表達式。? * 這個表達式返回sentry類的私有成員_Ok的值。? bool sentry::operator bool() const? * { // test if _Ipfx succeeded? * ? ? ? return (_Ok);? * ? }? * _Ok這個成員的值由sentry類的構造函數? * 在初始化時設置,設置的過程比較麻煩,這里不做贅述(事實上我也沒看十分明確)。? * 但能夠肯定的是,當輸入流的狀態是正常時,這個成員的值也是true,? * 反之,則是false。 ? * ? * _Count是調用者傳入的第二個參數,這里用做循環計數器的初值,以后每讀一個字
符,? * _Count的值會減一。?
****************************************************************************
**/ ? { ? // state okay, use facet to extract ?? int_type _Metadelim = _Traits::to_int_type(_Delim); ?? int_type _Meta = _Myios::rdbuf()->sgetc();//從輸入流讀一個字符 ?? for (; ; _Meta = _Myios::rdbuf()->snextc()) //snextc()從輸入流中讀取下一
個字符 ? if (_Traits::eq_int_type(_Traits::eof(), _Meta)) ?? {// end of file, quit ?? _State |= ios_base::eofbit; ?? break; ?? }//注:遇到文件尾,getline結束 ?? else if (_Meta == _Metadelim) { ? // got a delimiter, discard it and quit ?? ++_Chcount; ? ?//讀取字符數+1 ? _Myios::rdbuf()->sbumpc(); ? /*注:上面這句把結束符讀掉了,假設不指定結束符,那就是把'\n'讀掉了
。 ? 但回車符本身并沒有復制到緩沖區中,? 這樣下次的讀操作將從回車符后面的第一個字符開始,? */ ? break; ?? }/* 注:遇到結束符,getline結束,注意這里的順序,它是先推斷是否遇到結束
符,后推斷是否讀入了指定個數的。 */ ? else if (--_Count <= 0) ?? {// buffer full, quit ?? _State |= ios_base::failbit; ?? break; ?? } ? //注:讀到了指定個數,運行到這里已經隱含了在指定個數的最后一位仍然不是
結束符, ? //因此該部分將輸入流狀態置為了錯誤。 ? //這直接導致了接下來的getline(或者get)以及>>運算符等讀操作都不能正確執
行) ?? else { ? // got a character, add it to string ?? ++_Chcount; ?//讀取字符數加1 ? *_Str++ = _Traits::to_char_type(_Meta); ?? }//注:這一分支將讀取到的單個字符復制到緩沖區中 ? } ?? *_Str = _Elem(); ?// ? /* add terminating null character /*注:前面這句為字符串增加了終止符'\0'? 由于_Elem()構造了一個ascii碼為0的字符對象*/ ? _Myios::setstate(_Chcount == 0 ? _State | ios_base::failbit : _State); ? /*注:假設沒有讀入不論什么字符,要保持運行這一次getline之前的輸入流狀態,? 否則依據這一次getline運行的情況,設置輸入流為對應狀態。 */ ? return (*this); ? //返回輸入流對象本身 ? } ??
可是我認為這當中還是有問題,由于: sbumpc: advances the get pointer and returns the character pointed by it?
before the call. snextc: advances the get pointer and returns the character pointed by it?
after the call. 因為是傳引用,所以不論調用哪個,都會改變原文件流中get的指針所指向的位置。并且,
告訴大家一個更為驚奇的結果便是: 以下程序: int main(){ int n = 6; string tem; ifstream infile("test.txt"); for(int i = 0;i<n;i++){ getline(infile,tem); //getline(infile,tem,'\t'); cout<<tem<<endl; } return 0; } 的輸出結果為: abcd efgh ijk ijk ijk ijk
無論依照我的想法還是依照對上面源代碼的理解,結果都不應該是這個樣子。是源代碼錯了,還
是我的理解有問題?希望知道的朋友能指導一下。
========================================================================== 好吧,可能是編譯器的問題,用比的編譯器編譯執行了一下,結果和我的想法是一致的,跟源代碼所要表達的也是一致的 ,所以我原先的想法是沒錯的,結貼啦~ 所以假設你不斷的從文件流中getline的話,假設你想推斷是否已經達到文件結尾的話,那么僅僅需推斷getline所得到的字符串是否為 空就ok了~ 再補充一下,因為getline函數將istream參數作為返回值,和輸入操作符一樣也把它作為推斷條件。所以假設到達文件結尾的話,那么返回的文件流包括的字符為空,這個false是等價的 ,所以我們也能夠用while(getline(infile,str))來對文件流是否達到結尾進行判定。
總結
- 上一篇: 为什么有些产品不尽完美 但还是有大批用户
- 下一篇: STL之优先级队列priority_qu