getline函数

2023-10-30

在我的印象中,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文件如下所示:
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’留
下一个位置。其他地方二者基本相同。
原理想必也很简单。每一次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))来对文件流是否达到结尾进行判定。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

getline函数 的相关文章

随机推荐

  • 阿波罗无法通过链接外网

    官方提供说明 官方地址 无法连接外网的原因通常是阿波罗走到内网 通过注册中心无法连接到服务 bin bash apollo config db info apollo config db url jdbc mysql localhost 3
  • 【C++】初级—类和对象之赋值运算符重载、取地址操作符重载和const修饰的取地址运算符重载

    在上一篇中写到 一个类会产生的6种默认函数中的前三个默认函数 1 构造函数 2 析构函数 3 拷贝构造函数 4 赋值运算符重载 5 取地址操作符重载 6 拷贝构造函数 这次将着重讲解后三个函数 赋值运算符重载 运算符重载 什么是运算符重载呢
  • CSS字体的单位

    长度单位 1 像素 px 实际上是屏幕上的一个个小点 100px 100个小点 这个点 正常情况我们是看不到 如果我们把一个内容放大很多倍 就可以看到了 在pc端 电脑端 一般情况下1px 1个发光点 也是我们最常用的长度单位 它是固定单位
  • “区块链”技术在传统行业中的应用

    点击上方 中兴开发者社区 关注我们 每天读一篇一线开发者原创好文 比特币可能是一场庞氏骗局 但区块链技术却真实存在 2013年以来 比特币受到了全世界投资者的狂热追捧 虽然几经涨跌 大部分国家监管方对其态度也不甚明朗 但作为比特币底层技术的
  • react hooks实现原理(useState为例)

    一 源代码 逻辑十分绕 建议多敲几遍 let isMount true 判断是挂载还是更新 let workInProgressHook App组件对应的fiber对象 const fiber memorizedState null 当前h
  • 【NLP】T5:文本到文本转换器

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • 智能指针类HasPtr

    智能指针是存储指向动态分配对象指针的类 用于控制对象的生存期 能够确保自动正确的销毁动态分配的对象 防止内存泄露 HasPtr 在其他方面的行为与普通指针一样 具体而言 复制对象时 副本和原对象将指向同一基础对象 如果通过一个副本改变基础对
  • 微信小程序车牌号码输入(虚拟键盘)

    近日在网上看到一位博主写的微信小程序 输入车牌号 有新能源 原文链接 https blog csdn net qq706352062 article details 105554453 ops request misc 257B 2522r
  • ppt复现CVPR顶会流程图

    本次目标如下图 难点在于立方体和矩阵格网的绘制 文末附机器学习绘图模板 先来绘制立方体 插入 形状 立方体 调节成如下图 再点击水平翻转 绘制矩形 多绘制几个组合成矩形格网 右键设置形状和格式 输入以下参数 调整好使其贴合立方体 如图所示
  • 全志V3S环境编译开发流程

    这里使用的是荔枝派Zero 官网上面没有带spiflash的 首先准备一张SD卡 U BOOT 首先需要配置交叉编译环境 这里就不多说了 需要的话前往Sipeed官网 Sipeed 首先获取uboot源码 git clone https g
  • vscode快捷键:定位某一行,跳转到这一行

    快捷键 Ctrl G 然后在弹出的框中输入行数就可以了 参考 https blog csdn net cvper article details 81090028
  • Spring的三种注入方式:构造方法注入,set方法注入,注解注入

    本文演示三种值注入方式和三层模式下的注解注入获取对象 首先是搭建基本的Spring运行环境导入四个基本的核心jar包和两个日志包 在src根目录下新建bean xml的配置文件 同时引入dtd约束 一 构造函数注入 在bean xml中配置

  • 分割线样式

    hr style height 2px border none border top 2px ridge green
  • vue项目 后端传给base64格式图形验证码 ,前端进行解析,回显。

    我们在实际项目中时在登录的时候 时常会遇到图形验证码 来进行验证用户操作 什么是图形验证码 图形验证码是验证码的一种 有防止黑客对某一特定注册用户用程序暴力破解私人信息 恶意破解密码 刷论坛灌水的作用 票 图形验证码是一种区分用户是计算机还
  • SpringBoot底层原理

    SpringBoot底层原理 一 SpringBoot是什么 二 SpringBoot核心原理 三 springboot启动原理 一 SpringBoot是什么 想要了解springboot底层原理必须要先知道springboot是什么 作
  • 计算机视觉领域关注的会议和期刊

    原本为给师弟师妹总结的自己经验 节省计算机视觉领域大家看什么论文和去哪里看论文的困惑 一 会议论文 视觉的领域主要关注的三大顶会论文 CVPR ICCV ECCV 搜索途径 1 CVPR和ICCV都是IEEE库 可以在IEEE explor
  • QT中ui文件生成关联的C++类

    在VS2008中给对话框资源添加关联的C 类时 可通过右键菜单 添加类 直接添加关联的C 类 但QT中不支持这样的操作 在QT中在创建界面ui时 可手动也可自动创建ui文件关联的C 类 分别如下所示 一 自动创建ui文件和对应的C 类 项目
  • C语言开发网站

    在正式开发之前 先了解一下网站的原理 请求 处理 响应 在浏览器的network中可以看到浏览器和服务器的交互过程 请求一个网站的本质就是咱们的浏览器和服务器交互的一个过程 比如说咱们请求www baidu com 就是咱们的浏览器向服务器
  • lede 插件_路由器帮你签到!「LEDE/Openwrt系统“签到狗”插件使用教程」

    每日签到 废话不多说 用了才知道 图标 支持的站点 baidu 百度贴吧 百度文库v2ex V2EXhostloc hostloc comacfun A站bilibili B站163music 网易云音乐PCmiui 小米论坛52pojie
  • getline函数

    在我的印象中 getline函数经常出现在自己的视野里 模糊地记得它经常用来读取字符串 但是又对它的参数不是很了解 今天又用到了getline函数 现在来细细地总结一下 首先要明白设计getline函数的目的 其实很简单 就是从流中读取字符