C++IO流
一. 流 (Streams) 的核心概念
在 C++ 中,所有 I/O 操作都涉及流对象。流对象将程序与实际的 I/O 设备(如键盘、屏幕、文件或网络)隔离开来。
A. 基本流类
| 类名 | 描述 | 关键用途 |
|---|---|---|
std::istream |
输入流的抽象基类。 | 从设备读取数据。 |
std::ostream |
输出流的抽象基类。 | 向设备写入数据。 |
std::iostream |
既能输入又能输出的流。 | 文件读写、网络通信等。 |
B. 标准 I/O 流对象(在 <iostream> 中定义)
C++ 启动时,会自动创建并打开四个标准的流对象,它们是全局可用的:
| 对象 | 类型 | 描述 | 默认目标 |
|---|---|---|---|
std::cin |
std::istream |
标准输入流。 | 键盘 (Keyboard) |
std::cout |
std::ostream |
标准输出流。 | 屏幕 (Screen/Console) |
std::cerr |
std::ostream |
标准错误流。 | 屏幕 (无缓冲,用于立即显示错误) |
std::clog |
std::ostream |
标准日志流。 | 屏幕 (带缓冲,用于日志记录) |
二. 基本的输入/输出操作符
C++ 的 I/O 依赖于两个重载的运算符:
A. 插入运算符(Insertion Operator): <<
用于将数据插入到输出流(如 cout)中。
int a = 42; |
B. 提取运算符(Extraction Operator): >>
用于将数据从输入流(如 cin)中提取到变量中。int number;
std::cout << "Enter a number: ";
std::cin >> number; // 尝试从输入缓冲区读取一个整数到 number
三. 文件 I/O (File I/O)
要进行文件操作,需要包含 <fstream> 头文件。
1. 文件流的类和类型
文件 I/O 主要通过以下三个类实现,它们都继承自标准的 I/O 流:
| 类名 | 继承自 | 描述 | 主要用途 |
|---|---|---|---|
std::ofstream |
std::ostream |
文件输出流 (Output File Stream)。 | 用于写入数据到文件。 |
std::ifstream |
std::istream |
文件输入流 (Input File Stream)。 | 用于读取数据从文件。 |
std::fstream |
std::iostream |
文件输入/输出流。 | 用于同时读写同一个文件。 |
2. 打开文件:模式
当你创建文件流对象时,需要指定文件打开的模式(std::ios::openmode)。这些模式可以组合使用(通过位或运算符 |)。
| 模式标志 | 描述 | 默认设置 |
|---|---|---|
std::ios::in |
读模式。允许从文件读取数据。 | ifstream 的默认模式。 |
std::ios::out |
写模式。允许向文件写入数据。 如果文件已存在,会清空文件内容。 | ofstream 的默认模式。 |
std::ios::app |
追加模式 (Append)。所有写入操作都在文件末尾进行。 | |
std::ios::trunc |
截断模式 (Truncate)。如果文件存在,清空其内容(与 out 相同)。 |
ofstream 的默认行为包含此功能。 |
std::ios::binary |
二进制模式。不进行任何数据转换(如换行符 |
|
std::ios::ate |
定位到文件末尾 (At End)。文件打开后,立即将读写指针定位到文件末尾。 |
3. 文件操作的基本步骤
文件 I/O 的过程通常包含三个步骤:
步骤 A: 创建并打开文件
你可以通过构造函数或 open() 成员函数来打开文件。
方式 1: 构造函数(推荐)ofstream::ofstream(char* pFileName,int mode=ios::out,int prot=filebuf::openprot);
std::ofstream outFile("data.txt"); // 默认使用 std::ios::out 模式 |
方式 2: open() 函数std::ofstream outFile;
outFile.open("data.txt", std::ios::out);
步骤 B: 检查文件是否打开成功
在尝试读写之前,必须检查文件流对象是否处于可用状态。if (outFile.is_open()) {
// 文件打开成功,进行读写操作
} else {
std::cerr << "Error: Could not open the file." << std::endl;
}
// 也可以使用布尔运算符(更简洁):
if (outFile) { /* ... */ }
步骤 C: 读写数据和关闭文件
文件流对象的使用方式与 std::cin 和 std::cout 类似。
写入数据 (ofstream)
使用插入运算符 <<:outFile << "Score: " << 95 << std::endl;
读取数据 (ifstream)
格式化读取: 使用提取运算符
>>(读取到空白字符为止)。int score;
inFile >> score;逐行读取: 使用全局函数
std::getline()。std::string line;
while (std::getline(inFile, line)) {
// 处理 line
}
```
**关闭文件**
文件流对象离开作用域时会自动关闭,但显式调用 `close()` 是一个良好的习惯。
```C++
outFile.close();
四.I/O成员函数
1. put():输出单个字符
put() 函数是 std::ostream 类的成员函数,用于向输出流中写入一个字符。
A. 语法
std::ostream& put(char c); |
B. 描述
- 功能: 将字符
c写入到流中。 - 返回值: 返回流对象的引用(允许方法链式调用)。
- 用途: 最常用于直接输出单个字符,特别是在需要处理原始字节流或在自定义 I/O 处理器中。
C. 示例
|
2. get():输入单个字符或字符串
get() 函数是 std::istream 类的成员函数,用于从输入流中读取数据。它有几种重载形式,但最常用的是读取单个字符和读取一行(但不提取定界符)。
A. 语法 (常用重载)
| 形式 | 返回值/类型 | 描述 |
|---|---|---|
| 单个字符 (1) | int |
读取并返回流中的下一个字符。如果失败或 EOF,返回 EOF。 |
| 单个字符 (2) | istream& |
读取下一个字符并存储到 char& c 中。 |
| 字符数组 | istream& |
读取 delim 或读取 |
B.描述
- 与
>>的区别:get()不会跳过空白字符(如空格、制表符、换行符),而是将它们作为有效字符读取。 - 定界符处理: 读取字符数组时,
get()读取到定界符,但不将定界符从流中提取(定界符仍留在缓冲区中)。
C. 示例 (读取单个字符)
|
D. 示例 (读取字符串/行)
|
3. getline():输入一整行
getline() 函数是 std::istream 类的成员函数,用于从流中读取一整行数据。
A. 语法 (istream 成员函数)
std::istream& getline(char* s, std::streamsize n, char delim = '\n'); |
_注意:与 std::getline(std::istream&, std::string&, char)(全局函数,用于 std::string)不同,这是用于 C 风格字符串的成员函数。_
B. 描述
- 功能: 读取最多
个字符到 指向的缓冲区,直到遇到定界符 delim(默认为'\n')。 - 定界符处理:
getline()会读取定界符,并将其从流中提取和丢弃,但不会存入目标字符串。 - 用途: 这是读取 C 风格字符串或字符数组时,最常用的整行输入方法,因为它处理了定界符,不会像
get()那样在缓冲区中留下尾巴。
C. 示例
|
五.IO流控制
1)控制浮点数输出位数的核心方法
1. 基础:fixed + setprecision(n)
fixed:强制浮点数以固定小数位数的形式输出(而非科学计数法)。setprecision(n):配合fixed使用时,指定小数部分的位数(n 为位数)。
|
2. 不使用 fixed 时的 setprecision(n)
若不配合 fixed,setprecision(n) 控制的是整个浮点数的有效数字位数(包括整数部分):double num = 123.4567;
cout << setprecision(4) << num << endl; // 输出 123.5(共4位有效数字,四舍五入)
3. 恢复默认输出格式:resetiosflags
使用 fixed 或 setprecision 后,格式会一直生效,直到被重置:double pi = 3.1415926535;
cout << fixed << setprecision(2) << pi << endl; // 3.14
// 重置为默认格式(取消fixed,精度恢复默认)
cout << resetiosflags(ios::fixed) << setprecision(6) << pi << endl; // 3.14159(默认6位有效数字)
2)是否有输入检测
问题描述
- 输入包含多组测试样例,每组数据为一行,由一个或多个大写字母组成(用空格分隔)。
- 需要持续读取每行输入,直到输入结束(如
EOF),考察对循环读取多行数据的掌握。
代码
getline(cin, line) 是 C++ 中用于读取一行字符串的输入函数,定义在 <string> 头文件中,通常与标准输入流 cin 配合使用。它的核心功能是从输入流中读取一整行字符(包括空格),直到遇到换行符 \n 为止,并将读取的内容存储到字符串变量 line 中(换行符本身不会被存入字符串)。
istringstream iss(line);
- 作用:创建一个字符串输入流
iss,并将之前读取的整行字符串line作为该流的输入源。 - 用途:
istringstream是 C++ 标准库<sstream>中的类,它允许像操作输入流(如cin)一样操作字符串,方便按空格空格、制表符等分隔隔符拆分字符串。 - 举例:如果
line是"A B C D",iss就像同一个包含这些字符的输入流,后续可以用>>运算符逐个读取其中的子串("A"、"B"、"C"、"D")。 - 用
iss >> grade从字符串流中逐个读取子串(按空格拆分),存入grade中。
using namespace std;
int main() {
string line;
// 持续读取每行输入(多组测试样例)
while (getline(cin, line)) {
istringstream iss(line); // 用于拆分每行的字符串
string grade;
int total = 0; // 总绩点
int count = 0; // 有效字母个数
bool valid = true; // 标记是否所有字母都合法
// 拆分并处理每个字母
while (iss >> grade) {
// 只处理单个字符的等级(题目中为大写字母)
if (grade.size() != 1) {
valid = false;
break;
}
char c = grade[0];
switch (c) {
case 'A': total += 4; break;
case 'B': total += 3; break;
case 'C': total += 2; break;
case 'D': total += 1; break;
case 'F': total += 0; break;
default: // 非法字母
valid = false;
break;
}
if (!valid) break;
count++;
}
// 输出结果
if (valid && count > 0) {
double avg = static_cast<double>(total) / count;
cout << fixed << setprecision(2) << avg << endl;
} else {
cout << "Unknown" << endl;
}
}
return 0;
}
3)设置值的输出宽度
1. 使用 std::setw
std::setw(n) 会设置下一个输出字段的最小宽度为
- 头文件: 你需要包含
<iostream>和<iomanip>。 - 特性:
- 它只影响紧随其后的一个输出项。一旦该项输出完毕,输出宽度会恢复到默认的“所需宽度”。
- 如果输出值所需的宽度超过你设置的
, setw不会截断值,而是会打印整个值。 - 默认情况下,
std::setw会进行右对齐。
示例:
int main() {
int a = 123;
double b = 45.67;
std::string s = "Hello";
std::cout << "--- 宽度为 8 ---" << std::endl;
// 设置下一个输出项的宽度为 8
std::cout << std::setw(8) << a << std::endl;
// 每次使用前都需要重新设置 setw
std::cout << std::setw(8) << b << std::endl;
// 对字符串也有效
std::cout << std::setw(8) << s << std::endl;
std::cout << "--- 多个项在同一行 ---" << std::endl;
std::cout << "|" << std::setw(10) << "Value1"
<< "|" << std::setw(10) << "Value2"
<< "|" << std::endl;
std::cout << "|" << std::setw(10) << 100
<< "|" << std::setw(10) << 5.5
<< "|" << std::endl;
return 0;
}
输出:--- 宽度为 8 ---
123
45.67
Hello
--- 多个项在同一行 ---
| Value1| Value2|
| 100| 5.5|
2. 配合其他操作符使用
你可以使用其他操作符来改变对齐方式和填充字符:
| 操作符 | 作用 |
|---|---|
std::left |
左对齐 (默认为 std::right) |
std::right |
右对齐 (默认) |
std::setfill(char) |
设置用来填充多余宽度的字符 (默认为空格 ‘ ‘) |
4)强制显示小数点和符号
1.使用 std::showpoint强制显示小数点
std::showpoint: 强制在浮点数输出中包含小数点,即使其后没有数字需要显示。直到被 std::noshowpoint 取消。
示例:
int main() {
double value = 123.0;
std::cout << "默认输出: " << value << std::endl; // 输出: 123
std::cout << "使用 showpoint: ";
// 强制显示小数点。该设置会保持,直到被 noshowpoint 取消。
std::cout << std::showpoint << value << std::endl; // 输出: 123.0
return 0;
}
2. 强制显示符号
默认情况下,只有负数会显示负号 (-)。要强制正数也显示正号 (+),你需要使用 std::showpos。std::showpos: 强制在正数前面显示加号 (+)。一旦设置,它将永久保持,直到被 std::noshowpos 取消。
示例:
int main() {
int pos_int = 100;
int neg_int = -50;
double pos_float = 12.5;
std::cout << "--- 默认输出 ---" << std::endl;
std::cout << "正整数: " << pos_int << std::endl; // 输出: 100
std::cout << "负整数: " << neg_int << std::endl; // 输出: -50
std::cout << "--- 强制显示符号 ---" << std::endl;
// 强制显示正号
std::cout << std::showpos;
std::cout << "正整数: " << pos_int << std::endl; // 输出: +100
std::cout << "负整数: " << neg_int << std::endl; // 输出: -50 (不受影响)
std::cout << "正浮点数: " << pos_float << std::endl; // 输出: +12.5
// 恢复默认设置(可选)
std::cout << std::noshowpos;
return 0;
}