C++ Primer string流

发布于:2025-02-22 ⋅ 阅读:(13) ⋅ 点赞:(0)

欢迎阅读我的 【C++Primer】专栏

专栏简介:本专栏主要面向C++初学者,解释C++的一些基本概念和基础语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级程序设计技术。希望对读者有帮助!

在这里插入图片描述
在这里插入图片描述

8.3 string流

sstream头文件定义了三个类型来支持内存IO,这些类型可以向string写入数据,从string读取数据,就像string是一个IO流一样。

istringstream从string读取数据,ostringstream问string写入数据,而头文件stringstream既可从string读数据也可向string写数据。与fstream类型类似,头文件sstream中定义的类型都继承自我们已经使用过的iostream头文件中定义的类型。除了继承得来的操作,sstream中定义的类型还增加了一些成员来管理与流相关联的string。表8.5列出了这些操作,可以对stringstream对象调用这些操作,但不能对其他IO类型调用这些操作。

表8.5:stringstream特有的操作

sstream strm 是一个未绑定的stringstream对象。sstream是头文件sstream中定义的一个类型
sstream strm(s); strm是一个sstream对象,保存strings的一个拷贝。此构造函数是explicit的
strm.str() 返回strm所保存的string的拷贝
strm.str(s) 将strings拷贝到strm中。返回void

使用istringstream

当我们的某些工作是对整行文本进行处理,而其他一些工作是处理行内的单个单词时,通常可以使用istringstream考虑这样一个例子,假定有一个文件,列出了一些人和他们的电话号码。某些人只有一个号码,而另一些人则有多个一一家庭电话、工作电话、移动电话等。我们的输入文件

看起来可能是这样的:

morgan 2015552368 8625550123
drew 9735550130
lee 6095550132 2015550175 8005550000

文件中每条记录都以一个人名开始,后面跟随一个或多个电话号码。我们首先定义一个简单的类来描述输入数据:

//成员默认为公有;
struct PersonInfo{
string name;
vector<string>Phones;
}

类型PersonInfo的对象会有一个成员来表示人名,还有一个vector来保存此人的所有电话号码。我们的程序会读取数据文件,并创建一个PersonInfo的vector。vector中征个元素对应文件中的一条记录。我们在一个循环中处理输入数据,每个循环步读取一条记录,提取出一个人名和若干电话号码:

string line,word//分别保存来自输入的一行和单词
vector<PersonInfo>people;//保存来自输入的所有记录
//逐行从输入读取数据,直至cin遇到文件尾(或其他错误)
while_(getline(cin,1ine))
PersonInfo info;//创建一个保存此记录数据的对象
istringstream record(1ine);//将记录绑定到刚读入的行
record>>info.name;//读取名字
while(record>>word)//读取电话号码
    info.phones.push_back(word);//保持它们
people.push_back(info);//将此记录追加到people末尾

这里我们用getline从标准输入读取整条记录。如果getline调用成功,那么line中将保存着从输入文件而来的一条记录.在while中,我们定义了一个局部PersonInfo对象,来保存当前记录中的数据。

接下来我们将一个istringstream与刚刚读取的文本行进行绑定,这样就可以在此istringstream上使用输入运算符来读取当前记录中的每个元素。我们首先读取人名,随后用一个while循环读取此人的电话号码。

当读取完line中所有数据后,内层while循环就结束了。此循环的工作方式与前面章节中读取cin的循环很相似,不同之处是,此循环从一个string而不是标准输入读取数据。当string中的数据全部读出后,同样会触发"文件结束"信号,在record上的下一个输入操作会失败。

我们将刚刚处理好的PersonInfo追加到vector中,外层while循环的一个循环步就随之结束了。外层while循环会持续执行,直至遇到cin的文件结束标识。

使用ostringstream

当我们逐步构造输出,希望最后一起打印时,ostringstream是很有用的。例如,对上一节的例子,我们可能想逐个验证电话号码并改变其格式。如果所有号码都是有效的,我们希望输出一个新的文件,包含改变格式后的号码。对于那些无效的号码,我们不会将它们输出到新文件中,而是打印一条包含人名和无效号码的错误信息。

由于我们不希望输出有无效电话号码的人,因此对每个人,直到验证完所有电话号码后才可以进行输出操作。但是,我们可以先将输出内容“写入“到一个内存 ostringstream中:

for(const auto&entry:people){//对people中每一项
    ostringstream formatted,badkNums;//每个循环步创建的对象
    for(const auto&nums:entry.Phones){//对每个数
        if(!valid(nums)){
            badNums<<" "<<nums;//将数的字符定形式存入badNums
        } else
        //将格式化的字符串“写入“formatted
        formatted<<" "<<format(nums);
    }
    if(badNums.size().empty())//没有错误的数
        os<<entry.name<<" "//打印名字
            <<formatted.str()<<endl;//和格式化的数
    else//否则,打印名字和错误的数
        cerr<<"input error:"<<entry.name
            <<"invalid number(s)"<<badNums.str()<<endl;
}

在此程序中,我们假定已有两个函数,valid和format,分别完成电话号码验证和改变格式的功能。程序最有趣的部分是对字符串流formatted和badNums的使用。我们使用标准的输出运算符(<<)向这些对象写入数据,但这些“写入“操作实际上转换为string操作,分别向formatted和badkums中的string对象添加字符。