8.1.2节练习
练习8.1:
编写函数,接受一个istream&
参数,返回值类型也是istream&
。此函数须从给定流中读取数据,直至遇到文件结束标识时停止。它将读取的数据打印在标准输出上。完成这些操作后,在返回流之前,对流进行复位,使其处于有效状态。
#include <iostream>
using namespace std;
istream& func(istream & is){
string buf;
while(is>>buf)
cout<<buf<<endl;
is.clear();
return is;
}
int main() {
func(cin);
return 0;
}
练习8.2
问题:测试函数,调用参数为cin
。
练习8.3
问题:什么情况下,下面的while循环会终止?
while(cin>>i) /*...*/
当failbit 或者badbit 或者 eofbit 为1时,会终止。
8.2.1节练习
练习8.4
问题:编写函数,以读模式打开一个文件,将其内容读入到一个string
的vector
中,将每一行作为一个独立的元素存于vector
中。
void read_line(const string &filename,vector<string> &vec){
//创建一个文件流
ifstream ifs;
ifs.open(filename);
//判断是否文件打开成功
if(ifs) {
cout<<"Open file successfully"<<endl;
while (!ifs.eof()) {
//定义一个临时变量,用来读取数据并将数据传入到vector中
string temp;
getline(ifs, temp);
vec.push_back(temp);
}
}
}
练习8.5
问题:重写上面的程序,将每个单词作为一个独立的元素进行存储。 解:
void read_words(const string &filename,vector<string> &vec){
//创建一个文件流
ifstream ifs;
ifs.open(filename);
//判断是否文件打开成功
if(ifs) {
cout << "Open file successfully" << endl;
while (!ifs.eof()) {
//定义一个临时变量,用来读取数据并将数据传入到vector中
string temp;
ifs >> temp;
vec.push_back(temp);
}
}
}
练习8.6
问题:重写7.1.1节的书店程序,从一个文件中读取交易记录。将文件名作为一个参数传递给main
。
这个问题我在自己写的时候遇到了一些问题。之后参考github上的代码:
Sales_data.h
//
// Created by YUSEN JIA on 2020/10/19.
//
#ifndef C__P_U8_8_6_2__SALES_DATA_H
#define C__P_U8_8_6_2__SALES_DATA_H
#include <string>
#include <iostream>
class Sales_data {
friend std::istream &read(std::istream &is, Sales_data &item);
friend std::ostream &print(std::ostream &os, const Sales_data &item);
friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs);
public:
Sales_data() = default;
Sales_data(const std::string &s):bookNo(s) { }
Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ }
Sales_data(std::istream &is) { read(is, *this); }
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
private:
inline double avg_price() const;
private:
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
inline
double Sales_data::avg_price() const
{
return units_sold ? revenue/units_sold : 0;
}
// declarations for nonmember parts of the Sales_data interface.
std::istream &read(std::istream &is, Sales_data &item);
std::ostream &print(std::ostream &os, const Sales_data &item);
Sales_data add(const Sales_data &lhs, const Sales_data &rhs);
#endif //C__P_U8_8_6_2__SALES_DATA_H
Sales_data.cpp
//
// Created by YUSEN JIA on 2020/10/19.
//
#include "Sales_data.h"
// member functions.
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
// friend functions
std::istream &read(std::istream &is, Sales_data &item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
std::ostream &print(std::ostream &os, const Sales_data &item)
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue;
return os;
}
Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
main.cpp
#include <fstream>
#include <iostream>
#include"Sales_data.h"
using std::ifstream; using std::cout; using std::endl; using std::cerr;
int main(int argc, char **argv)
{
ifstream input(argv[1]);
Sales_data total;
if (read(input, total))
{
Sales_data trans;
while (read(input, 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;
}
return 0;
}
最后通过input文件,将要输入的参数放到input中即可。
8.2.2节练习
练习8.7
问题:修改上一节的书店程序,将结果保存到一个文件中。将输出文件名作为第二个参数传递给main
函数。
#include <iostream>
#include"Sales_data.h"
#include<fstream>
#include<vector>
using namespace std;
int main(int argc,char * argv[]) {
ifstream input(argv[1]);
ofstream output;
output.open(argv[2]);
Sales_data total;
if(input){
Sales_data trans;
while(read(input,trans)){
print(output, trans) << endl;
}
}
else{
cerr<<"No data!"<<endl;
}
return 0;
}
将上一节main程序修改成这个
练习8.8:
问题:修改上一题的程序,将结果追加到给定的文件末尾。对同一个输出文件,运行程序至少两次,检验数据是否得以保留。
#include <iostream>
#include"Sales_data.h"
#include<fstream>
#include<vector>
using namespace std;
int main(int argc,char * argv[]) {
ifstream input(argv[1]);
ofstream output;
output.open(argv[2],ofstream::app);
Sales_data total;
if(input){
Sales_data trans;
while(read(input,trans)){
print(output, trans) << endl;
}
}
else{
cerr<<"No data!"<<endl;
}
return 0;
}
这次给output加上app模式
最终连续运行程序两次后输出的结果:
可以看到上一次数据得到了保留
8.3.1节练习
练习8.9
问题:使用你为8.1.2节第一个练习所编写的函数打印一个istringstream
对象的内容。
#include <iostream>
#include<sstream>
using std::istream,std::cout,std::string,std::endl,std::cin;
using std::istringstream;
istream& func(istream & is){
string buf;
while(is>>buf)
cout<<buf<<" ";
is.clear();
return is;
}
int main() {
string ss("hello");
istringstream is(ss);
func(is);
return 0;
}
练习8.10
问题:编写程序,将来自一个文件中的行保存在一个vector
中。然后使用一个istringstream
从vector
读取数据元素,每次读取一个单词。
#include <iostream>
#include<sstream>
#include<vector>
#include<string>
#include<fstream>
using namespace std;
istream &read(istream &is,vector<string> &vec){
string temp;
getline(is,temp);
vec.push_back(temp);
return is;
}
int main() {
vector<string> line;
ifstream input("C++p_u8_8.10/input");//这里是存放input的路径
if(input){
while(read(input,line)){}
}
for(auto i=line.begin();i!=line.end();i++){
istringstream a(*i);
string buf;
a>>buf;
cout<<buf<<endl;
}
return 0;
}
练习8.11
问题:本节的程序在外层while
循环中定义了istringstream
对象。如果record
对象定义在循环之外,你需要对程序进行怎样的修改?重写程序,将record
的定义移到while
循环之外,验证你设想的修改方法是否正确
#include <iostream>
#include "PersonInfo.h"
#include<sstream>
using std::string;
using std::vector;
using std::cin;
using std::cout,std::istringstream;
int main() {
string line,word;
vector<PersonInfor> people;
istringstream record;
while(getline(cin,line) ){
PersonInfor info;
record.clear();//因为是继承自iostream,这里的clear是将流中所有的条件状态位复位,将流的状态设置为有效。
record.str(line);
record>>info.name;
while(record>>word){
info.phones.push_back(word);
}
people.push_back(info);
}
for(auto i:people){
i.print();
}
return 0;
}
如果这里缺少了clear,那么只能输出一行的值。需要每次对输入流复位。
练习8.12
问题:我们为什么没有在PersonInfo
中使用类内初始化?
答:因为这里只是需要聚合类,没必要对其类内初始化。
8.3.2节练习
练习8.13
问题:重写本节的电话号码程序,从一个命名文件而非cin
读取数据
main.cpp
#include <iostream>
#include "PersonInfo.h"
#include<sstream>
#include<fstream>
using std::string;
using std::vector;
using std::cin;
using std::cout,std::istringstream;
using std::istream;
using std::ifstream;
istream &read(istream &is,string ©){
getline(is,copy);
return is;
}
int main() {
ifstream input("/Users/jason/CLionProjects/C++p_u8_8.13/input");
string line,word;
vector<PersonInfor> people;
istringstream record;
while(read(input,line) ){
PersonInfor info;
record.clear();//因为是继承自iostream,这里的clear是将流中所有的条件状态位复位,将流的状态设置为有效。
record.str(line);
record>>info.name;
while(record>>word){
info.phones.push_back(word);
}
people.push_back(info);
}
for(auto i:people){
i.print();
}
return 0;
}
PersonInfo.cpp
#include "PersonInfo.h"
void PersonInfor::print()const{
std::cout<<"name:"<<this->name<<std::endl;
std::cout<<"Phone number:";
for(auto phone : this->phones){
std::cout<<phone<<std::endl;
}
std::cout<<std::endl;
}
PersonInfo.h
#include<iostream>
#include<vector>
#include<sstream>
struct PersonInfor{
void print()const;
std::string name;
std::vector<std::string> phones;
};
练习8.14
问题:我们为什么将entry
和nums
定义为const auto&
?
答:因为我们不希望修改people中的值,所以这里使用const。因为是类类型,避免拷贝,所以使用了引用。