目录
在 C++ 编程中,文件的输入输出(I/O)操作是一项非常重要的功能。它允许将程序中的数据保存到文件中,以便后续使用或与其他程序共享;同时,也可以从文件中读取数据,供程序进行处理。C++ 标准 IO 库提供了强大而灵活的文件操作功能,通过使用这些功能,可以轻松地实现文件的创建、读取、写入和修改等操作。本文将详细介绍 C++ 标准 IO 库中文件输入输出的相关知识,包括文件流对象的使用、文件的打开和关闭、数据的读写操作以及错误处理等方面。
一、文件流对象
1.1 文件流类的介绍
C++ 标准 IO 库中提供了三个主要的文件流类,分别用于不同类型的文件操作:
①ifstream
(输入文件流):
- 用途:用于从文件读取数据。
- 特点:默认以读模式打开文件,支持文本文件和二进制文件的读取操作。
②ofstream
(输出文件流):
- 用途:用于向文件写入数据。
- 特点:默认以写模式打开文件,支持文本文件和二进制文件的写入操作。
③fstream
(文件流):
- 用途:既可以读取数据,也可以写入数据。
- 特点:默认以读写模式打开文件,支持文本文件和二进制文件的读写操作。
这些类都定义在 <fstream>
头文件中,因此在使用文件流对象之前,需要包含该头文件。
1.2 文件流对象的创建
创建文件流对象的方式与创建其他对象类似,只需指定对象的类型和名称即可。以下是创建不同类型文件流对象的示例代码:
#include <fstream>
int main() {
// 创建一个用于读取文件的 ifstream 对象
std::ifstream inFile;
// 创建一个用于写入文件的 ofstream 对象
std::ofstream outFile;
// 创建一个既可以读取又可以写入文件的 fstream 对象
std::fstream ioFile;
return 0;
}
二、文件的打开和关闭
2.1 文件的打开
在使用文件流对象进行文件操作之前,需要先打开文件。可以使用文件流对象的 open()
成员函数来打开文件,该函数接受两个参数:文件名和打开模式。文件名可以是相对路径或绝对路径,打开模式指定了文件的打开方式,如只读、只写、追加等。以下是打开文件的示例代码:
#include <fstream>
#include <iostream>
int main() {
// 创建一个 ofstream 对象
std::ofstream outFile;
// 打开一个文件用于写入,如果文件不存在则创建它
outFile.open("example.txt", std::ios::out);
if (outFile.is_open()) {
std::cout << "文件打开成功!" << std::endl;
} else {
std::cout << "文件打开失败!" << std::endl;
}
// 关闭文件
outFile.close();
return 0;
}
使用 open()
函数以 std::ios::out
模式打开文件 example.txt
,该模式表示以写入方式打开文件。如果文件不存在,则会创建该文件;如果文件已经存在,则会清空文件内容。
2.2 打开模式
C++ 标准 IO 库定义了多种打开模式,这些模式可以通过按位或运算符 |
组合使用,以满足不同的需求。常见的打开模式如下表:
模式标志 | 说明 |
---|---|
std::ios::in |
读方式打开(ifstream默认),用于读取文件内容。 |
std::ios::out |
写方式打开(ofstream默认),如果文件不存在则创建它,如果文件已经存在则清空文件内容。 |
std::ios::binary |
二进制模式(默认文本模式) |
std::ios::ate |
打开文件后将文件指针定位到文件末尾,但仍然可以进行读写操作。 |
std::ios::app |
追加模式(自动定位到末尾),所有写入操作都会在文件末尾进行,不会清空文件原有内容。 |
std::ios::trunc |
截断文件(默认如果存在则清空) |
以下是使用组合打开模式的示例代码:
#include <fstream>
#include <iostream>
int main() {
// 以读写方式打开文件,如果文件不存在则创建它,并且不清空原有内容
std::fstream ioFile("example.txt", std::ios::in | std::ios::out | std::ios::app);
if (ioFile.is_open()) {
std::cout << "文件打开成功!" << std::endl;
} else {
std::cout << "文件打开失败!" << std::endl;
}
// 关闭文件
ioFile.close();
return 0;
}
2.3 文件的关闭
在完成文件操作后,需要关闭文件,以释放系统资源。可以使用文件流对象的 close()
成员函数来关闭文件。以下是关闭文件的示例代码:
#include <fstream>
#include <iostream>
int main() {
std::ofstream outFile("example.txt");
if (outFile.is_open()) {
// 写入数据到文件
outFile << "Hello, World!";
// 关闭文件
outFile.close();
std::cout << "文件已关闭!" << std::endl;
} else {
std::cout << "文件打开失败!" << std::endl;
}
return 0;
}
三、文件的读取操作
3.1 逐行读取文本文件
逐行读取文本文件是一种常见的文件读取方式,可以使用 std::getline()
函数来实现。该函数接受两个参数:文件流对象和一个字符串对象,用于存储读取到的一行文本。以下是逐行读取文本文件的示例代码:
#include <fstream>
#include <iostream>
#include <string>
int main() {
std::ifstream inFile("example.txt");
if (inFile.is_open()) {
std::string line;
while (std::getline(inFile, line)) {
std::cout << line << std::endl;
}
// 关闭文件
inFile.close();
} else {
std::cout << "文件打开失败!" << std::endl;
}
return 0;
}
使用 std::getline()
函数逐行读取文件 example.txt
的内容,并将每行内容存储在 line
字符串中,然后输出到控制台。
3.2 读取二进制文件
读取二进制文件时,需要以二进制模式打开文件,并使用 read()
成员函数来读取数据。read()
函数接受两个参数:一个指向存储数据的缓冲区的指针和要读取的字节数。以下是读取二进制文件的示例代码:
#include <fstream>
#include <iostream>
int main() {
std::ifstream inFile("example.bin", std::ios::binary);
if (inFile.is_open()) {
// 定义一个缓冲区
char buffer[1024];
// 读取数据到缓冲区
inFile.read(buffer, sizeof(buffer));
// 获取实际读取的字节数
std::streamsize bytesRead = inFile.gcount();
std::cout << "读取了 " << bytesRead << " 字节的数据。" << std::endl;
// 关闭文件
inFile.close();
} else {
std::cout << "文件打开失败!" << std::endl;
}
return 0;
}
以二进制模式打开文件 example.bin
,并使用 read()
函数将文件内容读取到 buffer
缓冲区中,最后使用 gcount()
函数获取实际读取的字节数。
四、文件的写入操作
4.1 写入文本文件
写入文本文件可以使用插入操作符 <<
或 write()
成员函数。插入操作符 <<
可以方便地将各种数据类型写入文件,而 write()
函数主要用于写入二进制数据。以下是使用插入操作符写入文本文件的示例代码:
#include <fstream>
#include <iostream>
int main() {
std::ofstream outFile("example.txt");
if (outFile.is_open()) {
// 写入字符串
outFile << "Hello, World!" << std::endl;
// 写入整数
int num = 123;
outFile << "The number is: " << num << std::endl;
// 关闭文件
outFile.close();
std::cout << "数据写入成功!" << std::endl;
} else {
std::cout << "文件打开失败!" << std::endl;
}
return 0;
}
使用插入操作符 <<
将字符串和整数写入文件 example.txt
。
4.2 写入二进制文件
写入二进制文件需要以二进制模式打开文件,并使用 write()
成员函数。write()
函数接受两个参数:一个指向要写入数据的缓冲区的指针和要写入的字节数。以下是写入二进制文件的示例代码:
#include <fstream>
#include <iostream>
int main() {
std::ofstream outFile("example.bin", std::ios::binary);
if (outFile.is_open()) {
// 定义一个整数数组
int numbers[] = {1, 2, 3, 4, 5};
// 计算数组的字节数
std::streamsize size = sizeof(numbers);
// 写入数据到文件
outFile.write(reinterpret_cast<const char*>(numbers), size);
// 关闭文件
outFile.close();
std::cout << "数据写入成功!" << std::endl;
} else {
std::cout << "文件打开失败!" << std::endl;
}
return 0;
}
以二进制模式打开文件 example.bin
,并使用 write()
函数将整数数组 numbers
的内容写入文件。
五、文件指针的操作
5.1 文件指针的概念
文件指针是一个指向文件中当前位置的指针,它决定了下一次读写操作的起始位置。在进行文件读写操作时,文件指针会自动向后移动。可以使用文件流对象的 tellg()
和 tellp()
函数来获取文件指针的当前位置,使用 seekg()
和 seekp()
函数来移动文件指针的位置。
5.2 获取文件指针的位置
tellg()
函数用于获取输入文件指针的当前位置,tellp()
函数用于获取输出文件指针的当前位置。这两个函数返回一个 std::streampos
类型的值,表示文件指针的位置。以下是获取文件指针位置的示例代码:
#include <fstream>
#include <iostream>
int main() {
std::fstream ioFile("example.txt", std::ios::in | std::ios::out);
if (ioFile.is_open()) {
// 获取输入文件指针的当前位置
std::streampos pos = ioFile.tellg();
std::cout << "输入文件指针的当前位置: " << pos << std::endl;
// 获取输出文件指针的当前位置
pos = ioFile.tellp();
std::cout << "输出文件指针的当前位置: " << pos << std::endl;
// 关闭文件
ioFile.close();
} else {
std::cout << "文件打开失败!" << std::endl;
}
return 0;
}
5.3 移动文件指针的位置
seekg()
函数用于移动输入文件指针的位置,seekp()
函数用于移动输出文件指针的位置。这两个函数接受两个参数:要移动的偏移量和偏移的起始位置。偏移的起始位置可以是 std::ios::beg
(文件开头)、std::ios::cur
(当前位置)或 std::ios::end
(文件末尾)。以下是移动文件指针位置的示例代码:
#include <fstream>
#include <iostream>
int main() {
std::fstream ioFile("example.txt", std::ios::in | std::ios::out);
if (ioFile.is_open()) {
// 将输入文件指针移动到文件开头偏移 5 个字节的位置
ioFile.seekg(5, std::ios::beg);
// 获取输入文件指针的当前位置
std::streampos pos = ioFile.tellg();
std::cout << "输入文件指针的当前位置: " << pos << std::endl;
// 将输出文件指针移动到文件末尾偏移 -3 个字节的位置
ioFile.seekp(-3, std::ios::end);
// 获取输出文件指针的当前位置
pos = ioFile.tellp();
std::cout << "输出文件指针的当前位置: " << pos << std::endl;
// 关闭文件
ioFile.close();
} else {
std::cout << "文件打开失败!" << std::endl;
}
return 0;
}
六、错误处理
在进行文件操作时,可能会遇到各种错误,如文件打开失败、读写错误等。可以使用文件流对象的状态标志来检查文件操作是否成功。常见的状态标志有:
good()
:检查流是否处于正常状态。eof()
:检查是否到达文件末尾。fail()
:检查是否发生了可恢复的错误。bad()
:检查是否发生了严重的错误。
以下是一个错误处理的示例代码:
#include <fstream>
#include <iostream>
int main() {
std::ifstream inFile("nonexistent.txt");
if (!inFile.good()) {
if (inFile.fail()) {
std::cout << "文件打开失败,可能是文件不存在。" << std::endl;
} else if (inFile.bad()) {
std::cout << "发生了严重的错误!" << std::endl;
}
} else {
// 进行文件操作
inFile.close();
}
return 0;
}
尝试打开一个不存在的文件,通过检查 good()
、fail()
和 bad()
状态标志来判断文件打开是否成功,并输出相应的错误信息。
七、总结
C++ 标准 IO 库提供了丰富的文件输入输出功能,通过使用文件流对象、合理选择打开模式、掌握文件的读写操作和文件指针的操作,以及进行错误处理,可以实现各种复杂的文件操作。在实际编程中,需要根据具体的需求选择合适的文件操作方式,并注意错误处理,以确保程序的健壮性和可靠性。
八、参考资料
- 《C++ Primer(第 5 版)》这本书是 C++ 领域的经典之作,对 C++ 的基础语法和高级特性都有深入讲解。
- 《Effective C++(第 3 版)》书中包含了很多 C++ 编程的实用建议和最佳实践。
- 《C++ Templates: The Complete Guide(第 2 版)》该书聚焦于 C++ 模板编程,而
using
声明在模板编程中有着重要应用,如定义模板类型别名等。 - C++ 官方标准文档:C++ 标准文档是最权威的参考资料,可以查阅最新的 C++ 标准(如 C++11、C++14、C++17、C++20 等)文档。例如,ISO/IEC 14882:2020 是 C++20 标准的文档,可从相关渠道获取其详细内容。
- cppreference.com:这是一个非常全面的 C++ 在线参考网站,提供了详细的 C++ 语言和标准库文档。
- LearnCpp.com:该网站提供了系统的 C++ 教程,配有丰富的示例代码和清晰的解释,适合初学者学习和理解相关知识。