zoukankan      html  css  js  c++  java
  • getline()函数

    在我的印象中,getline函数经常出现在自己的视野里,模糊地


    记得它经常用来读取字符串


    。但是又对它的参数不是很了解,今天又用到了getline函数,


    现在来细细地总结一下:


    首先要明白设计getline函数的目的,其实很简单,就是从流中


    读取字符串。而且读取的方


    式有很多,包括根据限定符,根据已读取的字符的个数。从这


    个函数的名称来看,它的直观


    意义是从流中读取一行,但是大家不要被这表面的现象所迷惑


    。其实如果让我来为这个函数


    去一个名字的话,或许我会取一个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。从函数声明中我们观察到两种函


    数声明的不同主要体现在参


    数的个数上,如果是两个参数的话,那么默认的限定符便


    是‘ ’了,但是如果声明了限


    定符,' '是否仍然有效呢?我写了如下程序做测试:


    int main(){
    int n = 6;
    string tem;
    ifstream infile("test.txt");
    for(int i = 0;i<n;i++){
    //getline(infile,tem);
    getline(infile,tem,' ');
    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个字


    符,因为最后要为‘’留


    下一个位置。其他地方二者基本相同。


    原理想必也很简单。每一次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,' ');
    cout<<tem<<endl;
    }
    return 0;
    }


    按照我的理解的话,那么文件中总共11个字母,当文件指针停


    在‘ ’之后,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();  
                /*注:上面这句把结束符读掉了,如果不指定结


    束符,那就是把' '读掉了


    。  
                但回车符本身并没有拷贝到缓冲区中, 
                这样下次的读操作将从回车符后面的第一个字符


    开始, 
                */  
                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 /*注:前面这句为


    字符串加入了终止符'' 
        因为_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,' ');
    cout<<tem<<endl;
    }
    return 0;
    }
    的输出结果为:
    abcd
    efgh
    ijk
    ijk
    ijk
    ijk


    不管按照我的想法还是按照对上面源码的理解,结果都不应该


    是这个样子。是源码错了,还


    是我的理解有问题?希望知道的朋友能指导一下。




    =======================================================


    ===================
    好吧,可能是编译器的问题,用比的编译器编译运行了一下,


    结果和我的想法是一致的,跟源码所要表达的也是一致的
    ,所以我原先的想法是没错的,结贴啦~
    所以如果你不断的从文件流中getline的话,如果你想判断是否


    已经达到文件结尾的话,那么只需判断getline所得到的字符串


    是否为
    空就ok了~
    再补充一下,由于getline函数将istream参数作为返回值,和


    输入操作符一样也把它作为判断条件。所以如果到达文件结尾


    的话,那么返回的文件流包含的字符为空,这个false是等价的 


    ,所以我们也可以用while(getline(infile,str))来对文件流


    是否达到结尾进行判定。


    http://blog.csdn.net/yelbosh/article/details/7483521
  • 相关阅读:
    C语言warning的收集和总结
    HLS:跑马灯实验
    HLS:OpenCV和RTL代码转换关系
    Zynq-7000 FreeRTOS(二)中断:串口Uart中断
    Zynq-7000 FreeRTOS(二)中断:PL中断请求
    Zynq-7000 FreeRTOS(二)中断:Timer中断
    xilinx DMA IP核(二) —— 文档阅读
    Zynq-7000 FreeRTOS(一)系统移植配置
    xilinx DMA IP核(一) —— loop测试 代码注释
    System Verilog基础(二)
  • 原文地址:https://www.cnblogs.com/zhurun/p/4590553.html
Copyright © 2011-2022 走看看