编写函数,接受一个 istream& 参数,返回值类型也是 istream&。此函数须从给定流中读取数据,直至遇到文件结束标识时停止。它将读取的数据打印在标准输出上。完成这些操作后,在返回流之前,对流进行复位,使其处于有效状态。
istream &func(istream &is)
{
string str;
while (!is.eof() && is >> str)
{
cout << str << endl;
}
is.clear();
return is;
}
测试函数,调用参数为cin。
#include
#include
using std::cin;
using std::cout;
using std::endl;
using std::istream;
using std::string;
istream &func(istream &is)
{
string str;
while (!is.eof() && is >> str)
{
cout << str << endl;
}
is.clear();
return is;
}
int main()
{
func(cin);
// 再次输入,检测流是否复位
string str;
while (cin >> str)
{
cout << str << endl;
}
return 0;
}
结果
1
1
2
2
^Z // 第一次输入结束标识后,还可以接着输入
2
2
^Z
什么情况下,下面的 while 循环会终止?
while (cin >> i) /*...*/
当输入的是一个错误的状态时,循环会终止;如 eofbit、failbit 和 badbit 任意一个被置位。
编写函数,以读模式打开一个文件,将其内容读入到一个 string 的 vector 中,将每一行作为一个独立的元素存于 vector 中。
#include
#include
#include
#include
using std::cerr;
using std::cout;
using std::endl;
using std::ifstream;
using std::string;
using std::vector;
int main()
{
ifstream in("data.txt");
vector<string> data;
string str;
if (in)
{
while (getline(in, str))
{
data.push_back(str);
}
}else
{
cerr << "无法打开文件 data.txt" << endl;
return 0;
}
for(const auto &s : data)
{
cout << s << endl;
}
}
重写上面的程序,将每个单词作为一个独立的元素进行存储。
#include
#include
#include
#include
using std::cerr;
using std::cout;
using std::endl;
using std::ifstream;
using std::string;
using std::vector;
int main()
{
ifstream in("data.txt");
vector<string> data;
string str;
if (in)
{
while (in>>str)
{
data.push_back(str);
}
}else
{
cerr << "无法打开文件 data.txt" << endl;
return 0;
}
for(const auto &s : data)
{
cout << s << endl;
}
}
重写 7.1.1 节的书店程序(第 229 页),从一个文件中读取交易记录。将文件名作为一个参数传递给 main(参见 6.2.5 节,第 196 页)。
#include"Sales_data.h"
#include
using std::cerr;
using std::ifstream;
int main(int argc,char *argv[])
{
// 使用 argv 中的实参,一定要记得可选的实参从 argv[1] 开始(第 197 页)
ifstream in(argv[1]);
if (in)
{
Sales_data total;
if (read(in,total))
{
Sales_data trans;
while (read(in,trans))
{
if (total.isbn()==trans.isbn())
{
total.combine(trans);
}
else
{
print(cout, total) << endl;
total = trans;
}
}
print(cout, total) << endl;
}else {
cerr<<" No data?!"<<endl;
}
}
else
{
cerr << "无法打开文件交易记录文件" << endl;
return -1;
}
return 0;
}
修改上一节的书店程序,将结果保存到一个文件中。将输出文件名作为第二个参数传递给 main 函数。
#include"Sales_data.h"
#include
using std::cerr;
using std::ifstream;
using std::ofstream;
int main(int argc,char *argv[])
{
ifstream in(argv[1]);
ofstream out(argv[2]);
if (in)
{
Sales_data total;
if (read(in,total))
{
Sales_data trans;
while (read(in,trans))
{
if (total.isbn()==trans.isbn())
{
total.combine(trans);
}
else
{
print(out, total) << endl;
total = trans;
}
}
print(out, total) << endl;
}else {
cerr<<" No data?!"<<endl;
}
}
else
{
cerr << "无法打开文件交易记录文件" << endl;
return -1;
}
return 0;
}
修改上一题的程序,将结果追加到给定的文件末尾。对同一个输出文件,运行程序至少两次,检验数据是否得以保留。
#include"Sales_data.h"
#include
using std::cerr;
using std::ifstream;
using std::ofstream;
int main(int argc,char *argv[])
{
ifstream in(argv[1]);
ofstream out(argv[2], ofstream::app);
if (in)
{
Sales_data total;
if (read(in,total))
{
Sales_data trans;
while (read(in,trans))
{
if (total.isbn()==trans.isbn())
{
total.combine(trans);
}
else
{
print(out, total) << endl;
total = trans;
}
}
print(out, total) << endl;
}else {
cerr<<" No data?!"<<endl;
return -1;
}
}
else
{
cerr << "无法打开文件交易记录文件" << endl;
return -1;
}
return 0;
}
运行
8.7 indata.txt outdata.txt
结果
// outdata.txt
001 10 160 16
002 3 46.5 15.5
003 6 111 18.5
第二次运行结果
// outdata.txt
001 10 160 16
002 3 46.5 15.5
003 6 111 18.5
001 10 160 16
002 3 46.5 15.5
003 6 111 18.5
// 保留了文件第一次运行的原数据
使用你为 8.1.2 节(第 281 页)第一个练习所编写的函数打印一个 istringstream 对象的内容。
#include
#include
#include
using std::cout;
using std::endl;
using std::istream;
using std::string;
istream &func(istream &is)
{
string str;
while (!is.eof() && is >> str)
{
cout << str << endl;
}
is.clear();
return is;
}
int main()
{
std::istringstream istr("aaaaa\n\aaaaaaa");
func(istr);
return 0;
}
编写程序,将来自一个文件中的行保存在一个 vector 中。然后使用一个 istringstream 从 vector 读取数据元素,每次读取一个单词。
#include
#include
#include
#include
#include
using std::cerr;
using std::cout;
using std::endl;
using std::ifstream;
using std::istringstream;
using std::string;
using std::vector;
int main()
{
ifstream inf("words.txt");
if (inf)
{
string str;
vector<string> strLine;
while (getline(inf,str))
{
strLine.push_back(str);
}
for (const auto &s : strLine)
{
istringstream words(s);
while (words >> str)
{
cout << str << endl;
}
}
}
else
{
cerr << "文件打开失败!" << endl;
return -1;
}
return 0;
}
本节的程序在外层 while 循环中定义了 istringstream 对象。如果 record 对象定义在循环之外,你需要对程序进行怎样的修改?重写程序,将 record 的定义移到 while 循环之外,验证你设想的修改方法是否正确。
#include
#include
#include
#include
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;
struct PersonInfo
{
string name;
vector<string> phones;
};
int main()
{
string line, word;
vector<PersonInfo> people;
std::istringstream record;
while (getline(cin,line))
{
PersonInfo info;
record.str(line);
record >> info.name;
while (record >> word)
{
info.phones.push_back(word);
}
record.clear();// 定义在 while 循环之外,循环结束没有被清理,需要手动清理
people.push_back(info);
}
for (const auto &p : people)
{
cout << p.name << endl;
for (const auto &ph : p.phones)
{
cout << ph << endl;
}
}
return 0;
}
我们为什么没有在 PersonInfo 中使用类内初始化。
不确定每个人号码的数量,使用默认初始化就好。
重写本节的电话号码程序,从一个命名文件而非 cin 读取数据。
#include
#include
#include
#include
#include
using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;
struct PersonInfo
{
string name;
vector<string> phones;
};
bool valid(const string &s)
{
for (const auto &n : s)
{
if (!isdigit(n))
{
return false;
}
}
return true;
}
string format(const string &s)
{
return s;
}
int main()
{
std::ifstream in("PersonInfo.txt");
// std::ofstream out("PersonInfoCorrect.txt");
if (in)
{
string line, word;
vector<PersonInfo> people;
while (getline(in,line))
{
PersonInfo info;
std::istringstream record(line);
record >> info.name;
while (record >> word)
{
info.phones.push_back(word);
}
people.push_back(info);
}
// for (const auto &p : people)
// {
// cout << p.name << endl;
// for (const auto &ph : p.phones)
// {
// cout << ph << endl;
// }
// }
for (const auto &entry : people)
{
std::ostringstream formatted, badNums;
for (const auto &nums : entry.phones)
{
if (!valid(nums))
{
badNums << " " << nums;
}
else
{
formatted << " " << format(nums);
}
}
if (badNums.str().empty())
{
cout << entry.name << " " << formatted.str() << endl;
}
else
{
cerr << "错误输入:" << entry.name << " " << badNums.str() << endl;
}
}
}
else
{
cerr << "无法打开文件!" << endl;
}
return 0;
}
PersonInfo.txt
Johe 112211
Jim 1212 6565
KK 123H7
Aily 6728 1U82I
Mike 4355
Lily 44212 9872 74133
结果
Johe 112211
Jim 1212 6565
错误输入:KK 123H7
错误输入:Aily 1U82I
Mike 4355
Lily 44212 9872 74133
我们为什么将 entry 和 nums 定义为 const auto&?
1.它们都是类类型,因此使用引用避免拷贝,提高了效率;
2.在循环当中不会改变它们的值,因此用 const。