---
title: "数据输入与输出"
description: "从环境准备到 STL，系统学习 C++ 编程的基础知识。"
---

> Documentation Index
> Fetch the complete documentation index at: https://cppguide.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# 数据输入与输出

![](https://img.dailyup.xyz/originals/8208463153368-bdac634a-b33b-431c-8603-f7ae481273ca.png)


一个标准的 C++ 程序，基本上都会涉及到数据的输入和输出，可以把我们要写的程序想像成一个黑盒子，一端输入数据，经过程序的各种魔法处理，再输出处理后的数据。

前面的程序中，我们已经有使用 `cin` 来读入数据，使用 `cout` 来输出数据，而且我们也知道了它们是属于标准输入输出流中的一部分。

## 标准输入输出

### `cin` 与 `cout` 

要使用 `cin` 与 `cout`，需要引入 `iostream` 头文件。`iostream` 是 input / output stream 的缩写，它是 C++ 中用于数据输入与输出的标准头文件，属于 C++ 标准函数库的一部分。

```cpp
#include <iostream>
using namespace std;

int main() {
	int a, b, c;
	cin >> a >> b >> c;
	cout << "三数之和为 " << a + b + c << endl;
}
```

`endl` 代表 end line，简单说就是换行，也可以换成 `\n`，是同样的效果。

### 按行读入字符串

假设有下面一段代码去读入字符串：

```cpp
#include <iostream>
using namespace std;

int main() {
  string s;
  cin >> s;
  
  cout << s << endl;
  return 0;
}
```

输入数据

```
hello world!
```

运行程序，得到

```
hello
```

可以发现，后面的 `world!` 没有读进来，这是因为在默认情况下，无论使用 `cin` 还是 `scanf()` 来读入数据，在遇到空格或换行后都会停止读入。

如果要读入完整一行内容（包括空格），可以使用`getline()`。

```cpp
#include <iostream>
using namespace std;

int main() {
  string s;
  getline(cin, s); // 按行读入（包括当前行的所有内容）
  
  cout << s << endl;
  return 0;
}
```

运行程序，输出结果正确。

```
hello world!
```

#### 空行读入的问题

偶尔我们还会同时使用 `cin` 和 `getline()` 去读入数据，这里就很容易遇到读入空行的问题。

比如说，我们希望读入下面这样的数据，一行是年龄，一行是姓名：

```
18
Jordan Chan
```

我们很容易写出下面这段代码：

```cpp
#include <iostream>
using namespace std;

int main() {
  int age;
  cin >> age;
  
  string name;
  getline(cin, name);
  
  cout << age << endl;
  cout << name << endl;
  return 0;
}
```

运行程序，与期待的结果不一样，发现第二行字符串 `Jordan Chan` 没有读入进来。

这是因为，当我们使用 `cin` 读完 18 就停下来了（停在 18 与换行符之间），当使用 `getline()` 读入时，会首先读入 18 后面的换行符，然后就停下来，因此多读入了一个空行，反而真正想读的下一行数据没有读入进来。

**解决办法**，在 `cin` 后添加语句 `cin.ignore()`，忽略掉 `cin` 后面的换行符。

```cpp
#include <iostream>
using namespace std;

int main() {
  int age;
  cin >> age;
  cin.ignore(); // 忽略 cin 后面的换行符
  
  string name;
  getline(cin, name);
  
  cout << age << endl;
  cout << name << endl;
  return 0;
}
```

###  `scanf()` 与`printf()`

要使用`scanf()` 与`printf()` ，需要引入 `cstdio` 头文件。`cstdio` 是将 C 中的 `stdio.h` 内容以 C++ 头文件的形式表示出来的写法，`stdio.h` 是 C 标准函数库中的头文件，是 standard buffered input & ouput 的缩写。

下面的代码我们使用了 `scanf()` 来读入数据，使用 `printf()` 来输出数据。

```cpp
#include <cstdio>
using namespace std;

int main() {
	int a, b, c;
	scanf("%d %d %d", &a, &b, &c);

	printf("三数之和为 %d\n", a + b + c);
}
```


这里双引号中的百分号 `%`，代表后面紧跟的是格式化指令，不同的变量类型，对应不同的格式指令，下面是几组常见的格式化类型指令。

- `%d`，这里的 `d` 代表 decimal，表示以整数输出
- `%f` ，这里的 `f` 代表 float，表示以浮点数输出
- `%lf`，这里的 `lf` 代表 double，表示以双精度浮点数输出
- `%s`，这里的 `s` 代表 string，表示以字符串输出
- `%c`，这里的 `c` 代表 character，表示以字符输出

格式化指令当然不止这些，记住一些常用的就行了，在实际使用中，那些不常见的指令可以去查阅相应的文档。

下面两种情况，建议使用 `scanf() / printf()` ：
- 一些带复杂的格式化输出，使用 `printf()` 更为方便
- 需要读取的数据很大时，使用 `scanf()` 读入，效率会更高一些

> 一般来说，在使用 `cin` 或 `cout` 的地方，一定可以替换为 `scanf` 或 `printf`，反过来就不行！


## 格式化输出

当我们在输入数据时，很多时候都会涉及到结果的格式化输出。

来看一个简单的例子。

```cpp
#include <iostream>
using namespace std;

int main() {
    float a, b;
    cin >> a >> b;
    
    cout << a / b << endl;
    
    return 0;
}
```

下面是几组输入输出数据。

输入数据 1

```
2 3
```

输出数据 1

```
0.666667
```

输入数据 2

```
20 3
```

输出数据 2

```
6.66667
```

输入数据 3

```
200 3
```

输出数据 3

```
66.6667
```

如果将程序改为 `scanf` 和 `printf` 输入输出，代码如下所示：

```cpp
#include <iostream>
using namespace std;

int main() {
    float a, b;
    scanf("%f %f", &a, &b);
    
    printf("%f", a / b);
    
    return 0;
}
```

同样的上面三组输入数据，结果如下。

输入数据 1

```
2 3
```

输出数据 1

```
0.666667
```

输入数据 2

```
20 3
```

输出数据 2

```
6.666667
```

输入数据 3

```
200 3
```

输出数据 3

```
66.666664
```

可以发现它们之间的区别。

- 当我们用 `cout` 输出时，结果默认保留 6 位数字（除开小数点后的所有数字）
- 当我们用 `printf` 输出时，结果默认保留小数点后 6 位数字

在实际应用中，我们如何去控制输出数据的格式呢，比如，如果想结果保留小数点后两位，该怎么做？

### `cout` 进行格式化输出

```cpp
#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    const float PI = 3.1415926; // 定义为常量

    cout << fixed << setprecision(2) << PI << endl;

    return 0;
}
```

这里有两点需要注意：

1. 要进行格式化输出，需要引入 `<iomanip>` 头文件，或者使用万能头文件
2. `setprecision` 用于设置精度，但精度所代表的具体含义是由 `fixed` 来确定的，加上 `fixed`，表示保留小数点后两位，如果没有 `fixed`，表示除开小数点后保留两位数。

### `printf` 格式化输出

用 `printf()` 实现同样的保留小数点后两位输出，代码如下：

```cpp
#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    const float PI = 3.1415926; // 定义为常量

    printf("%.2f", PI);

    return 0;
}
```


这里的 `.2f` 表示结果以浮点数（`float`）输出，并且小数点后保留两位，相比 `cout` 的格式化输出更加简洁、直观。

### 更复杂的格式化输出

对于一些比较复杂的格式化输出，使用 `printf()` 可能会更方便一些。

例如，我们想希望输入两个整数 $a$ 和 $b$，按下面的指定格式输出。

输入：

```
5 3
```

输出：

```
a + b = 8, a - b = 2
```

如果使用 `cout` 输出，代码如下：

```cpp
#include <iostream>
#include <cstdio>
using namespace std;

int main() {
  int a, b;
  cin >> a >> b;

  cout << "a + b = " << a + b << ", " << "a - b = " << a - b << endl;

  return 0;
}
```


使用 `printf()` 输出，代码如下：

```cpp


```
```cpp
#include <iostream>
#include <cstdio>
using namespace std;

int main() {
  int a, b;
  cin >> a >> b;

  printf("a + b = %d, a - b = %d\n", a + b, a - b);

  return 0;
}
```

可以直观看到，使用 `printf()` 的格式化输出同样会更简洁一些。

## 小结

在本章节中，我们快速梳理了一下 C++ 中与输入输出相关的基本知识点，在实际应用中，关于输入输出的内容还有很多，比如更多的格式化输出、说文件读入与输出等高级主题，对于初学者和刚入门的竞赛选手来说，掌握上面的这些知识点基本上够用了。

Source: https://cppguide.xyz/basic/io/index.mdx
