getline函数

it2025-09-27  89

在我的印象中,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文件例如以下所看到的: a b c d e f g h i j k 如今先尝试全局函数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; } 的输出结果为: a b c d e f g h i j k i j k i j k i j k 无论依照我的想法还是依照对上面源代码的理解,结果都不应该是这个样子。是源代码错了,还 是我的理解有问题?希望知道的朋友能指导一下。 ========================================================================== 好吧,可能是编译器的问题,用比的编译器编译执行了一下,结果和我的想法是一致的,跟源代码所要表达的也是一致的 ,所以我原先的想法是没错的,结贴啦~ 所以假设你不断的从文件流中getline的话,假设你想推断是否已经达到文件结尾的话,那么仅仅需推断getline所得到的字符串是否为 空就ok了~ 再补充一下,因为getline函数将istream參数作为返回值,和输入操作符一样也把它作为推断条件。所以假设到达文件结尾的话,那么返回的文件流包括的字符为空,这个false是等价的 ,所以我们也能够用while(getline(infile,str))来对文件流是否达到结尾进行判定。

转载于:https://www.cnblogs.com/bhlsheji/p/4008137.html

相关资源:数据结构—成绩单生成器
最新回复(0)