C++ 文件与流
C++ 文件和流
Section titled “C++ 文件和流”到目前为止,我们使用了 <iostream> 标准库,它提供了 std::cin 和 std::cout 用于从标准输入读取和向标准输出写入。为了处理文件,C++ 提供了 <fstream> 库。
<fstream> 库定义了三种主要的文件 I/O 数据类型:
| 数据类型 | 描述 |
|---|---|
std::ofstream | 输出文件流(Output File Stream):用于创建文件和向文件写入数据。 |
std::ifstream | 输入文件流(Input File Stream):用于从文件读取数据。 |
std::fstream | 文件流(File Stream):结合了 ofstream 和 ifstream 的功能,用于读写文件。 |
要在 C++ 中执行文件处理,必须包含 <fstream> 头文件。通常也会包含 <iostream> 用于控制台交互,以及 <string> 用于处理文本。
打开文件(现代方法)
Section titled “打开文件(现代方法)”在现代 C++ 中,打开文件最简单的方式是在流对象构造时进行。这使用了 RAII(资源获取即初始化,Resource Acquisition Is Initialization)原则,意味着当流对象超出其作用域时(例如,在函数结束时),文件会自动关闭。
std::ofstream outputFile("mydata.txt"); // Opens for writing (truncates if exists) // 打开用于写入(如果文件存在则截断)std::ifstream inputFile("mydata.txt"); // Opens for reading // 打开用于读取std::fstream ioFile("mydata.txt", std::ios::in | std::ios::out); // Opens for read/write // 打开用于读/写应该始终检查文件是否成功打开:
std::ofstream outputFile("mydata.txt");if (!outputFile) { // Or !outputFile.is_open() // 或者 !outputFile.is_open() std::cerr << "Error opening file for writing!" << std::endl; // Handle error, maybe return or exit // 处理错误,可能返回或退出}使用 open() 和模式打开:
Section titled “使用 open() 和模式打开:”或者,可以先创建流对象,然后调用 open() 成员函数。第二个参数指定打开 模式(Mode):
void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out); // ofstream default // ofstream 默认void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in); // ifstream default // ifstream 默认常见的文件模式(定义在 std::ios_base 中):
| 模式标志 | 描述 |
|---|---|
std::ios::app | 追加(Append)到文件末尾。 |
std::ios::ate | 打开文件后定位到文件末尾(Seek to the end)。 |
std::ios::binary | 以二进制模式(Binary Mode)打开(而不是文本模式)。 |
std::ios::in | 打开用于读取(ifstream 的默认模式)。 |
std::ios::out | 打开用于写入(ofstream 的默认模式)。 |
std::ios::trunc | 如果文件存在,则将其长度截断(Truncate)为零(ofstream 在不使用 app 或 in 时的默认模式)。 |
使用位或运算符(|)组合多种模式。例如,以二进制模式打开用于输出,如果文件存在则追加内容:
std::ofstream outfile;outfile.open("data.bin", std::ios::out | std::ios::binary | std::ios::app);如果使用 RAII 方法(在构造函数中打开),文件将在流对象销毁时自动关闭。这是首选方法,因为它不易出错。
如果使用了 open() 方法,可以使用 close() 显式关闭文件。然而,依赖 RAII 通常更好。
// Only needed if RAII wasn't used or if you need to check close status// 仅当未使用 RAII 或需要检查关闭状态时才需要if (outfile.is_open()) { outfile.close();}使用流插入运算符(<<)与 ofstream 或 fstream 对象,就像使用 std::cout 一样:
std::ofstream outputFile("greeting.txt");if (outputFile) { std::string name = "Alice"; int age = 30; outputFile << "Hello, " << name << "!\n"; outputFile << "Your age is: " << age << std::endl;} // File automatically closed here // 文件在此处超出作用域时自动关闭从文件读取:
Section titled “从文件读取:”使用流提取运算符(>>)与 ifstream 或 fstream 对象,类似于 std::cin。要逐行读取,使用 std::getline()。
// Reading whitespace-separated values// 读取由空白字符分隔的值std::ifstream inputFile("data.txt");int value1;double value2;std::string word;if (inputFile) { inputFile >> value1 >> value2 >> word; // Check if reads were successful: if (!inputFile) { handle error... } // 检查读取是否成功: if (!inputFile) { 处理错误... }}
// Reading line by line// 逐行读取std::ifstream textFile("document.txt");std::string line;if (textFile) { while (std::getline(textFile, line)) { // Reads until newline or EOF // 读取直到换行符或文件末尾(EOF) std::cout << "Read line: " << line << std::endl; }} // File automatically closed here // 文件在此处超出作用域时自动关闭重要提示:混合使用 >> 和 std::getline() 需要小心,因为 >> 通常会在流中留下换行符,这可能导致 std::getline() 读取到空行。在使用 >> 之后、std::getline() 之前,你可能需要使用 inputFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');。
读写示例(现代化):
Section titled “读写示例(现代化):”这个程序会询问用户的姓名和年龄,将它们写入 user_data.txt 文件,然后重新读取数据并显示。
#include <fstream>#include <iostream>#include <string>#include <limits> // Required for numeric_limits // numeric_limits 所需
int main() { std::string name; int age; const std::string filename = "user_data.txt";
// --- Writing to the file --- // --- 写入文件 --- { std::ofstream outfile(filename); // Opens for writing (RAII style) // 打开用于写入(RAII 风格) if (!outfile) { std::cerr << "Error opening " << filename << " for writing." << std::endl; // 错误:无法打开文件 " << filename << " 用于写入。 return 1; }
std::cout << "Writing to the file " << filename << std::endl; // 正在写入文件 " << filename << std::endl; std::cout << "Enter your name: "; // 输入你的姓名: std::getline(std::cin, name); // Read full name with spaces // 读取包含空格的完整姓名
std::cout << "Enter your age: "; // 输入你的年龄: std::cin >> age;
// Ignore the newline character left by std::cin >> age // 忽略 std::cin >> age 留下的换行符 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Write data to the file // 将数据写入文件 outfile << name << std::endl; // Write name on its own line // 将姓名写入单独一行 outfile << age << std::endl; // Write age on its own line // 将年龄写入单独一行
std::cout << "Data written successfully." << std::endl; // 数据写入成功。 // outfile is automatically closed when it goes out of scope here // outfile 在此处超出作用域时自动关闭 }
std::cout << "\nReading from the file " << filename << std::endl; // \n 正在从文件 " << filename << " 读取。
// --- Reading from the file --- // --- 从文件读取 --- { std::ifstream infile(filename); // Opens for reading (RAII style) // 打开用于读取(RAII 风格) if (!infile) { std::cerr << "Error opening " << filename << " for reading." << std::endl; // 错误:无法打开文件 " << filename << " 用于读取。 return 1; }
std::string read_name; int read_age;
// Read data from the file // 从文件读取数据 if (std::getline(infile, read_name) && (infile >> read_age)) { // Display the data // 显示数据 std::cout << "Name read: " << read_name << std::endl; // 读取的姓名: std::cout << "Age read: " << read_age << std::endl; // 读取的年龄: } else { std::cerr << "Error reading data from the file." << std::endl; // 错误:从文件读取数据失败。 } // infile is automatically closed when it goes out of scope here // infile 在此处超出作用域时自动关闭 }
return 0;}示例交互:
Writing to the file user_data.txtEnter your name: Ada LovelaceEnter your age: 36Data written successfully.
Reading from the file user_data.txtName read: Ada LovelaceAge read: 36文件位置指针:
Section titled “文件位置指针:”输入流(istream)和输出流(ostream)都维护一个 位置指示器(Position Indicator),指定下一个读/写位置。可以使用 seekg(seek get,用于输入流)和 seekp(seek put,用于输出流)来操作它们。
这些函数通常接受一个 偏移量(Offset)(通常是 std::streamoff,一个类似于 long long 的类型)和一个可选的方向(std::ios_base::seekdir):
std::ios::beg:相对于流开始位置的偏移量(默认)。std::ios::cur:相对于当前位置的偏移量。std::ios::end:相对于流结束位置的偏移量。
输入流 fileObject 的示例用法:
// Position to the 10th byte from the beginning// 定位到从开始位置起的第 10 个字节fileObject.seekg(10, std::ios::beg); // or fileObject.seekg(10); // 或者 fileObject.seekg(10);
// Move 5 bytes forward from the current position// 从当前位置向前移动 5 个字节fileObject.seekg(5, std::ios::cur);
// Position 8 bytes back from the end// 从末尾向后移动 8 个字节fileObject.seekg(-8, std::ios::end);
// Position at the end of the file// 定位到文件末尾fileObject.seekg(0, std::ios::end);
// Get current position// 获取当前位置std::streampos currentPos = fileObject.tellg();seekp 和 tellp 是输出流(ostream)对应的函数。在文本模式下使用定位时要小心,因为偏移量的解释可能因系统而异,这取决于换行符转换。在二进制模式(std::ios::binary)下进行定位更可靠。
对于更高级的文件系统操作,如创建目录、检查文件是否存在或遍历目录内容,请考虑使用 C++17 的 <filesystem> 库。