学而时习之:C++ 中的文件处理

C++ 文件处理

文件处理是指利用 C++ 标准库 提供的类,从文件(如 .txt、.csv 等) 从文件中读取数据向文件中写入数据

程序在内存(RAM)中运行,因此数据仅在程序运行期间存在;程序结束后,RAM 中的所有数据会自动丢失。

文件处理允许将数据存储到二级存储(如 HDD 或 SSD)中,从而永久保存,即使程序终止后仍可再次访问。

C++ 在头文件 `` 中提供了文件流类:ofstreamifstreamfstream,用于文件操作。

打开文件

在对文件进行读写之前,必须先打开它。打开文件会把文件加载到内存中。在 C++ 中,通过创建文件流对象来完成这一步:

cpp 复制代码
fstream str("filename.ext", mode);

其中:

  • str:给该流对象起的名字
  • filename:文件名
  • mode:文件打开模式,指明我们将如何与该文件交互(读、写、追加等)

文件打开模式

文件打开模式指明文件是以 "读""写" 还是 "追加" 的方式打开。下面列出 C++ 中所有可用的文件模式:

模式 说明
ios::in 以读取方式打开文件。若文件不存在,打开失败。
ios::out 以写入方式打开文件:流缓冲区支持输出操作。
ios::binary 以二进制模式进行读写,而非文本模式。
ios::ate 打开后文件指针直接定位到文件末尾(at end)。
ios::app 所有写操作都在文件末尾进行,即追加写入。
ios::trunc 打开时清空文件原有内容(截断)。

示例

  1. 只读打开:
cpp 复制代码
fstream filein("file.txt", ios::in);
  1. 只写打开:
cpp 复制代码
fstream fileout("file.txt", ios::out);
  1. 模式可借助按位或 | 组合使用。例如同时支持读写:
cpp 复制代码
fstream str("file.txt", ios::in | ios::out);

补充说明

  • 写模式 (ios::out) 打开时,若文件不存在,系统会自动创建新文件。
  • 读模式 (ios::in) 打开时,若文件不存在,不会创建 新文件,且会抛出异常(或设置错误标志,需通过 fail() 等函数检测)。

其它文件流

fstream 并不是 C++ 提供的唯一文件流类,还有两个更专用的流类型:

流类 含义 默认等效模式
ifstream input file stream(输入文件流) 相当于以 ios::in 模式打开的 fstream
ofstream output file stream(输出文件流) 相当于以 ios::out 模式打开的 fstream

这两个类的默认模式无法被移除 ,但可以通过按位或 | 追加其它模式进行组合。

使用示例

  1. 只读打开文件:
cpp 复制代码
ifstream filein("file.txt");   // 默认已带 ios::in
  1. 只写打开文件:
cpp 复制代码
ofstream fileout("file.txt");  // 默认已带 ios::out

向文件写入数据

当文件通过 fstreamofstream 以写模式打开后,就可以像用 cout 一样,借助 << 运算符把数据写入文件。

示例代码:

cpp 复制代码
#include 
using namespace std;

int main() {
    // 打开文件(若不存在则自动创建)
    ofstream file(&#34;GFG.txt&#34;);
    
    // 将字符串写入文件
    file << &#34;Welcome to GeeksforGeeks.&#34;;

    return 0;
}

从文件读取数据

当文件通过 fstreamifstream 以读模式打开后,就可以像用 cin 一样,借助 >> 运算符从文件中读取数据。

示例代码:

cpp 复制代码
#include 
using namespace std;

int main() {
    // 以读模式打开文件
    ifstream file(&#34;GFG.txt&#34;);
    string s;

    // 从文件读取字符串(遇到空格结束)
    file >> s;

    cout << &#34;Read String: &#34; << s;

    return 0;
}
arduino 复制代码
Read String: Welcome

这跟 cin 存在同样的问题:用 >> 读取时,遇到第一个空白字符就停止。若想读取整行(包含空格),可使用 getline() 函数,如下所示:

cpp 复制代码
#include 
using namespace std;

int main() {
    // 以读模式打开文件
    ifstream file(&#34;GFG.txt&#34;);
    string s;

    // 从文件读取一整行
    getline(file, s);

    cout << &#34;Read String: &#34; << s;

    return 0;
}
vbnet 复制代码
Read String: Welcome to GeeksforGeeks.

关闭文件

关闭文件意味着关闭关联的流,并释放正在使用的资源。在完成文件操作后,尤其是在长时间运行的程序中,关闭文件非常重要,以避免内存泄漏、数据丢失等问题。

在 C++ 中,文件通过所有文件流中都提供的 close() 成员函数来关闭。

cpp 复制代码
#include 
using namespace std;

int main()
{
    // 以读取模式打开文件
    ifstream file(&#34;GFG.txt&#34;);
    string s;

    // 从文件中读取字符串
    getline(file, s);

    cout << &#34;读取的字符串: &#34; << s;

    // 关闭文件
    file.close();

    return 0;
}
makefile 复制代码
读取的字符串: Welcome to GeeksforGeeks.

文件处理中的错误

在文件处理过程中,可能会发生多种不同类型的错误,例如文件未找到、磁盘已满等。我们的程序应预见到这些常见错误,并能妥善地处理它们。以下是一些在文件处理过程中可能发生的常见错误:

1.文件打开失败

在某些情况下,由于各种原因,文件可能无法打开,例如文件不存在,或者程序没有权限打开它等。在这种情况下,我们可以使用文件流类的 is_open() 成员函数来检查文件是否成功打开。

cpp 复制代码
#include 
using namespace std;

int main() {
    fstream file(&#34;不存在的文件.txt&#34;, ios::in);

    // 检查文件是否已打开
    if (!file.is_open()) {
        cerr << &#34;错误:无法打开文件!&#34; << endl;
        return 1;
    }

    file.close();
    return 0;
}
复制代码
错误:无法打开文件!

2.读/写数据失败

另一个常见的错误是由于诸如模式不正确等原因导致无法读取或写入数据。在这种情况下,我们可以在每次读/写操作后进行验证。例如,可以按如下方式验证使用 getline() 的读取操作是否成功:

cpp 复制代码
#include 
using namespace std;

int main() {
    fstream file(&#34;GFG.txt&#34;, ios::out);

    if (!file.is_open()) {
        cerr << &#34;错误:无法打开文件!&#34; << endl;
        return 1;
    }
    string line;
    
    // 检查 getline() 是否成功读取数据
    if (!getline(file, line))
        cerr << &#34;错误:读取数据失败&#34; << endl;

    file.close();
    return 0;
}
复制代码
错误:读取数据失败

3.文件结束(EOF)错误

如果试图读取超出文件末尾的内容,就会引发 EOF 错误。这通常发生在读取前未检查文件是否已结束的情况下。我们可以使用 eof() 成员函数来判断是否到达了文件末尾。

cpp 复制代码
#include 
using namespace std;

int main()
{
    ifstream file(&#34;GFG.txt&#34;);

    if (!file.is_open())
    {
        cerr << &#34;错误:无法打开文件!&#34; << endl;
        return 1;
    }
    string line;
    while (getline(file, line))
        cout << line << endl;

    // 检查是否到达文件末尾
    if (file.eof())
        cout << &#34;已到达文件末尾。&#34; << endl;
    else
        cerr << &#34;错误:文件读取失败!&#34; << endl;

    file.close();
    return 0;
}
复制代码
已到达文件末尾。

请注意,在检查 EOF 之前,我们先验证了读取操作是否成功,因为即使读取失败,getline() 也会返回空值。

处理二进制文件

在 C++ 中,我们也可以处理二进制文件 ,这类文件以"原始字节"形式存储数据。要对二进制文件进行读写,必须在创建或打开文件时加上 ios::binary 标志。

向二进制文件写入数据

要把数据写入二进制文件,首先需要以 ios::binary 模式打开或创建文件。

cpp 复制代码
#include 
#include 
#include 
using namespace std;

int main()
{
    string str = &#34;Welcome to GeeksForGeeks&#34;;

    // 以二进制模式打开文件用于写入
    ofstream file(&#34;fileBin.bin&#34;, ios::binary);

    // 检查文件是否成功打开
    if (!file)
    {
        cerr << &#34;打开文件失败,无法写入。&#34;;
        return 1;
    }

    // 先将字符串长度的(字节数)写入文件
    size_t strLength = str.length();
    file.write(reinterpret_cast(&strLength), sizeof(strLength));

    // 再将字符串本身写入二进制文件
    file.write(str.c_str(), strLength);

    // 关闭文件
    file.close();

    return 0;
}

从二进制文件读取数据

与写入时一样,读取二进制文件也必须使用 ios::binary 标志,并以输入模式 ios::in 打开。

语法:

cpp 复制代码
fstream fileInstance(&#34;fileName.bin&#34;, ios::in | ios::binary);

完整示例:

cpp 复制代码
#include 
#include 
#include 
using namespace std;

int main()
{
    string str;

    // 以二进制读模式打开文件
    fstream file(&#34;fileBin.bin&#34;, ios::in | ios::binary);

    // 检查文件是否成功打开
    if (!file)
    {
        cerr << &#34;打开文件失败,无法读取。&#34;;
        return 1;
    }

    // 先读取字符串长度(字节数)
    size_t strLength;
    file.read(reinterpret_cast(&strLength), sizeof(strLength));

    // 为字符串分配内存并读取数据
    char *buffer = new char[strLength + 1]; // +1 用于末尾的 '\0'
    file.read(buffer, strLength);

    // 手动添加字符串结束符
    buffer[strLength] = '\0';

    // 将缓冲区转为 string
    str = buffer;

    // 输出文件内容
    cout << &#34;文件数据: &#34; << str;

    delete[] buffer;
    file.close();

    return 0;
}
makefile 复制代码
文件数据: Welcome to GeeksForGeeks

其他文件操作

我们还可以在 C++ 程序中对文件进行更多操作。常见的文件操作包括:

  • 删除文件
  • 在已有文件末尾追加字符串
  • 将一个文件的内容复制到另一个文件
相关推荐
天赐学c语言2 小时前
12.10 - 合并两个有序链表 && 对字节对齐的理解
数据结构·c++·leetcode·链表
仰泳的熊猫2 小时前
1092 To Buy or Not to Buy
数据结构·c++·算法·pat考试
CSDN_RTKLIB2 小时前
解除vcpkg对VS的全局配置注入
c++
君义_noip2 小时前
信息学奥赛一本通 4017:【GESP2309三级】小杨的储蓄 | 洛谷 B3867 [GESP202309 三级] 小杨的储蓄
c++·算法·gesp·信息学奥赛
渡我白衣2 小时前
计算机组成原理(4):计算机的层次结构与工作原理
运维·c语言·网络·c++·人工智能·笔记·硬件架构
kkk_皮蛋2 小时前
深入理解 WebRTC 临界锁实现与 C++ RAII 机制
开发语言·c++·webrtc
JANGHIGH3 小时前
c++ 多线程(一)
开发语言·c++
神仙别闹3 小时前
基于C++生成树思想的迷宫生成算法
开发语言·c++·算法
C语言小火车3 小时前
红黑树(C/C++ 实现版)—— 用 “带配重的书架” 讲透本质
c语言·开发语言·c++·红黑树