這個代碼的注釋參考了許多網友的心得,也加了一些我自己的體會,供後來者參考。
C++標準庫basic_istream類的getline成員函數有兩個版本
basic_istream<Elem, Tr>& getline( char_type *_Str, streamsize _Count);basic_istream<Elem, Tr>& getline( char_type *_Str, streamsize _Count, char_type _Delim);
第一個參數為使用者提供的字元數組首地址,第二個為數組長度,第三個為字串分隔符號。
第一個版本的成員函數的一個實現為
typedef basic_istream<_Elem, _Traits> _Myt;typedef basic_ios<_Elem, _Traits> _Myios;_Myt& getline(_Elem *_Str, streamsize _typedef basic_istream<_Elem, _Traits> _Myt;Count){// get up to _Count characters into NTCS, discard newline return (getline(_Str, _Count, _Myios::widen('\n')));}可見它最終還是調用了第二個版本的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); //返回輸入資料流對象本身}