sairate 4108f6c42d docs(book): 添加现代 C++教程及相关代码
- 新增现代 C++ 教程的 Preface 章节,包括英文和中文版本
- 添加 C++ Primer 练习代码
- 新增 Learn C++ 教程的 C++ 开发简介章节
- 添加头文件解析文档
- 更新 mkdocs.yml,包含新教程的目录结构
- 修改项目设置,使用 Python 3.10环境
2025-07-06 16:14:43 +08:00

418 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
sort: 8
---
# IO库
## 练习8.1
> 编写函数,接受一个`istream&`参数,返回值类型也是`istream&`。此函数须从给定流中读取数据,直至遇到文件结束标识时停止。它将读取的数据打印在标准输出上。完成这些操作后,在返回流之前,对流进行复位,使其处于有效状态。
解:
```cpp
std::istream& func(std::istream &is)
{
std::string buf;
while (is >> buf)
std::cout << buf << std::endl;
is.clear();
return is;
}
```
## 练习8.2
> 测试函数,调用参数为`cin`。
解:
```cpp
#include <iostream>
using std::istream;
istream& func(istream &is)
{
std::string buf;
while (is >> buf)
std::cout << buf << std::endl;
is.clear();
return is;
}
int main()
{
istream& is = func(std::cin);
std::cout << is.rdstate() << std::endl;
return 0;
}
```
## 练习8.3
> 什么情况下,下面的`while`循环会终止?
```cpp
while (cin >> i) /* ... */
```
解:
遇到文件结束符、或者IO流错误或读入了无效数据。
## 练习8.4
> 编写函数,以读模式打开一个文件,将其内容读入到一个`string`的`vector`中,将每一行作为一个独立的元素存于`vector`中。
解:
```cpp
void ReadFileToVec(const string& fileName, vector<string>& vec)
{
ifstream ifs(fileName);
if (ifs)
{
string buf;
while (getline(ifs, buf))
vec.push_back(buf);
}
}
```
## 练习8.5
> 重写上面的程序,将每个单词作为一个独立的元素进行存储。
解:
```cpp
void ReadFileToVec(const string& fileName, vector<string>& vec)
{
ifstream ifs(fileName);
if (ifs)
{
string buf;
while (ifs >> buf)
vec.push_back(buf);
}
}
```
## 练习8.6
> 重写7.1.1节的书店程序,从一个文件中读取交易记录。将文件名作为一个参数传递给`main`。
解:
```cpp
#include <fstream>
#include <iostream>
#include "../ch07/ex7_26.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;
}
```
## 练习8.7
> 修改上一节的书店程序,将结果保存到一个文件中。将输出文件名作为第二个参数传递给`main`函数。
解:
```cpp
#include <fstream>
#include <iostream>
#include "../ch07/ex7_26.h"
using std::ifstream; using std::ofstream; using std::endl; using std::cerr;
int main(int argc, char **argv)
{
ifstream input(argv[1]);
ofstream output(argv[2]);
Sales_data total;
if (read(input, total))
{
Sales_data trans;
while (read(input, trans))
{
if (total.isbn() == trans.isbn())
total.combine(trans);
else
{
print(output, total) << endl;
total = trans;
}
}
print(output, total) << endl;
}
else
{
cerr << "No data?!" << endl;
}
return 0;
}
```
## 练习8.8
> 修改上一题的程序,将结果追加到给定的文件末尾。对同一个输出文件,运行程序至少两次,检验数据是否得以保留。
解:
```cpp
#include <fstream>
#include <iostream>
#include "../ch07/ex7_26.h"
using std::ifstream; using std::ofstream; using std::endl; using std::cerr;
int main(int argc, char **argv)
{
ifstream input(argv[1]);
ofstream output(argv[2], ofstream::app);
Sales_data total;
if (read(input, total))
{
Sales_data trans;
while (read(input, trans))
{
if (total.isbn() == trans.isbn())
total.combine(trans);
else
{
print(output, total) << endl;
total = trans;
}
}
print(output, total) << endl;
}
else
{
cerr << "No data?!" << endl;
}
return 0;
}
```
## 练习8.9
> 使用你为8.1.2节第一个练习所编写的函数打印一个`istringstream`对象的内容。
解:
```cpp
#include <iostream>
#include <sstream>
using std::istream;
istream& func(istream &is)
{
std::string buf;
while (is >> buf)
std::cout << buf << std::endl;
is.clear();
return is;
}
int main()
{
std::istringstream iss("hello");
func(iss);
return 0;
}
```
## 练习8.10
> 编写程序,将来自一个文件中的行保存在一个`vector`中。然后使用一个`istringstream`从`vector`读取数据元素,每次读取一个单词。
解:
```cpp
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
using std::vector; using std::string; using std::ifstream; using std::istringstream; using std::cout; using std::endl; using std::cerr;
int main()
{
ifstream ifs("../data/book.txt");
if (!ifs)
{
cerr << "No data?" << endl;
return -1;
}
vector<string> vecLine;
string line;
while (getline(ifs, line))
vecLine.push_back(line);
for (auto &s : vecLine)
{
istringstream iss(s);
string word;
while (iss >> word)
cout << word << endl;
}
return 0;
}
```
## 练习8.11
> 本节的程序在外层`while`循环中定义了`istringstream`对象。如果`record`对象定义在循环之外,你需要对程序进行怎样的修改?重写程序,将`record`的定义移到`while`循环之外,验证你设想的修改方法是否正确。
解:
```cpp
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using std::vector; using std::string; using std::cin; using std::istringstream;
struct PersonInfo {
string name;
vector<string> phones;
};
int main()
{
string line, word;
vector<PersonInfo> people;
istringstream record;
while (getline(cin, line))
{
PersonInfo info;
record.clear();
record.str(line);
record >> info.name;
while (record >> word)
info.phones.push_back(word);
people.push_back(info);
}
for (auto &p : people)
{
std::cout << p.name << " ";
for (auto &s : p.phones)
std::cout << s << " ";
std::cout << std::endl;
}
return 0;
}
```
## 练习8.12
> 我们为什么没有在`PersonInfo`中使用类内初始化?
解:
因为这里只需要聚合类就够了,所以没有必要在`PersionInfo`中使用类内初始化。
## 练习8.13
> 重写本节的电话号码程序,从一个命名文件而非`cin`读取数据。
解:
```cpp
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
using std::vector; using std::string; using std::cin; using std::istringstream;
using std::ostringstream; using std::ifstream; using std::cerr; using std::cout; using std::endl;
using std::isdigit;
struct PersonInfo {
string name;
vector<string> phones;
};
bool valid(const string& str)
{
return isdigit(str[0]);
}
string format(const string& str)
{
return str.substr(0,3) + "-" + str.substr(3,3) + "-" + str.substr(6);
}
int main()
{
ifstream ifs("../data/phonenumbers.txt");
if (!ifs)
{
cerr << "no phone numbers?" << endl;
return -1;
}
string line, word;
vector<PersonInfo> people;
istringstream record;
while (getline(ifs, line))
{
PersonInfo info;
record.clear();
record.str(line);
record >> info.name;
while (record >> word)
info.phones.push_back(word);
people.push_back(info);
}
for (const auto &entry : people)
{
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 << "input error: " << entry.name
<< " invalid number(s) " << badNums.str() << endl;
}
return 0;
}
```
## 练习8.14
> 我们为什么将`entry`和`nums`定义为`const auto&`
解:
它们都是类类型,因此使用引用避免拷贝。
在循环当中不会改变它们的值,因此用`const`