一. 流 (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;
std::cout << "The value is: " << a << std::endl;
// std::endl 也是一个操作符,用于插入换行符并刷新缓冲区。

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 模式
std::ifstream inFile("data.txt", std::ios::in);

// 组合模式:以追加和二进制模式打开
std::fstream logFile("log.bin", std::ios::out | std::ios::app | std::ios::binary);

方式 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::cinstd::cout 类似。

写入数据 (ofstream)
使用插入运算符 <<

outFile << "Score: " << 95 << std::endl;

读取数据 (ifstream)

  1. 格式化读取: 使用提取运算符 >>(读取到空白字符为止)。

    int score;
    inFile >> score;
  2. 逐行读取: 使用全局函数 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. 示例

#include <iostream>

void demo_put() {
std::cout.put('H'); // 写入 H
std::cout.put('e'); // 写入 e
std::cout.put('\n'); // 写入换行符
}
// 输出: H
// e

2. get():输入单个字符或字符串

get() 函数是 std::istream 类的成员函数,用于从输入流中读取数据。它有几种重载形式,但最常用的是读取单个字符和读取一行(但不提取定界符)。

A. 语法 (常用重载)

形式 返回值/类型 描述
单个字符 (1) int 读取并返回流中的下一个字符。如果失败或 EOF,返回 EOF
单个字符 (2) istream& 读取下一个字符并存储到 char& c 中。
字符数组 istream& 读取 个字符到 ,直到遇到定界符 delim 或读取 个字符。

B.描述

  • >> 的区别: get() 不会跳过空白字符(如空格、制表符、换行符),而是将它们作为有效字符读取。
  • 定界符处理: 读取字符数组时,get() 读取到定界符,但不将定界符从流中提取(定界符仍留在缓冲区中)。

C. 示例 (读取单个字符)

#include <iostream>

void demo_get_char() {
char c1 = std::cin.get(); // 读取第一个字符 (可能是空格)
char c2;
std::cin.get(c2); // 读取第二个字符

// 假设输入: A B <Enter>
// c1 = 'A'
// c2 = ' ' (空格)
}

D. 示例 (读取字符串/行)

#include <iostream>

void demo_get_string() {
char buffer[20];

// 读取最多 19 个字符,直到遇到换行符 '\n' (默认定界符)
std::cin.get(buffer, 20);

// 如果用户输入 "Hello World<Enter>"
// buffer = "Hello World"
// 换行符 '\n' 仍留在输入缓冲区中!
}

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. 示例

#include <iostream>

void demo_getline() {
char name[50];

std::cout << "Enter your full name: ";
// 读取一行,直到 '\n' 被读取并丢弃
std::cin.getline(name, 50);

std::cout << "Welcome, " << name << "!\n";
}

五.IO流控制

1)控制浮点数输出位数的核心方法

1. 基础:fixed + setprecision(n)

  • fixed:强制浮点数以固定小数位数的形式输出(而非科学计数法)。
  • setprecision(n):配合 fixed 使用时,指定小数部分的位数(n 为位数)。
#include <iostream>
#include <iomanip> // 必须包含此头文件

using namespace std;

int main() {
double pi = 3.1415926535;

cout << fixed << setprecision(2) << pi << endl; // 输出 3.14(保留2位小数)
cout << fixed << setprecision(4) << pi << endl; // 输出 3.1416(保留4位小数)

return 0;
}

2. 不使用 fixed 时的 setprecision(n)

若不配合 fixedsetprecision(n) 控制的是整个浮点数的有效数字位数(包括整数部分):

double num = 123.4567;
cout << setprecision(4) << num << endl; // 输出 123.5(共4位有效数字,四舍五入)

3. 恢复默认输出格式:resetiosflags

使用 fixedsetprecision 后,格式会一直生效,直到被重置:

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 中。
    #include <iostream>
    #include <iomanip>
    #include <string>
    #include <sstream>

    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 会进行右对齐

示例:

#include <iostream>
#include <iomanip> // 必须包含这个头文件

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 取消。

示例:

#include <iostream>

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 取消。

示例:

#include <iostream>

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;
}