C++入门教程:第八篇 - 文件I/O操作

C++入门教程:第八篇 - 文件I/O操作

文件I/O(输入/输出)是程序与外部存储设备进行数据交换的关键操作。在C++中,文件I/O操作由标准库提供的流类完成。通过这些流类,程序可以读写文件,处理文件内容。本文将介绍C++中的文件I/O基础,包括如何打开、读写和关闭文件。

1. 文件流基础

C++提供了几种文件流类,用于处理不同类型的文件操作。主要的文件流类包括ifstream(输入文件流)、ofstream(输出文件流)和fstream(文件流)。ifstream用于从文件中读取数据,ofstream用于向文件写入数据,而fstream则支持读写操作。

1.1 打开文件

在进行文件操作之前,必须打开文件。文件流类提供了构造函数和open方法来打开文件。

1.1.1 使用构造函数打开文件

以下示例展示了如何使用ifstreamofstream的构造函数来打开文件:

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    // 打开输入文件
    ifstream inputFile("input.txt");

    if (!inputFile.is_open()) {
        cerr << "Error: Could not open input file" << endl;
        return 1;
    }

    // 打开输出文件
    ofstream outputFile("output.txt");

    if (!outputFile.is_open()) {
        cerr << "Error: Could not open output file" << endl;
        return 1;
    }

    // 关闭文件
    inputFile.close();
    outputFile.close();

    return 0;
}

cpp

1.1.2 使用open方法打开文件

你还可以使用open方法来打开文件:

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    ifstream inputFile;
    inputFile.open("input.txt");

    if (!inputFile) {
        cerr << "Error: Could not open input file" << endl;
        return 1;
    }

    ofstream outputFile;
    outputFile.open("output.txt");

    if (!outputFile) {
        cerr << "Error: Could not open output file" << endl;
        return 1;
    }

    // 关闭文件
    inputFile.close();
    outputFile.close();

    return 0;
}

cpp

2. 读取文件

从文件中读取数据可以使用ifstream类。C++标准库提供了多种方法来读取文件内容,如按行读取、按字符读取等。

2.1 按行读取文件

使用getline函数可以按行读取文件内容,并存储到一个string对象中。

 

cpp

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main() {
    ifstream inputFile("input.txt");

    if (!inputFile.is_open()) {
        cerr << "Error: Could not open input file" << endl;
        return 1;
    }

    string line;
    while (getline(inputFile, line)) {
        cout << line << endl;
    }

    inputFile.close();
    return 0;
}

cpp

2.2 按字符读取文件

使用流提取运算符>>可以按字符读取文件内容:

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    ifstream inputFile("input.txt");

    if (!inputFile.is_open()) {
        cerr << "Error: Could not open input file" << endl;
        return 1;
    }

    char ch;
    while (inputFile.get(ch)) {
        cout << ch;
    }

    inputFile.close();
    return 0;
}

cpp

2.3 读取文件到数组

将文件内容读取到一个数组或容器中,适用于处理较小的文件:

 

cpp

#include <iostream>
#include <fstream>
#include <vector>

using namespace std;

int main() {
    ifstream inputFile("input.txt");

    if (!inputFile.is_open()) {
        cerr << "Error: Could not open input file" << endl;
        return 1;
    }

    vector<string> lines;
    string line;
    while (getline(inputFile, line)) {
        lines.push_back(line);
    }

    for (const auto& l : lines) {
        cout << l << endl;
    }

    inputFile.close();
    return 0;
}

cpp

3. 写入文件

向文件中写入数据可以使用ofstream类。C++标准库提供了多种方法来写入数据,包括按行写入、按字符写入等。

3.1 按行写入文件

使用<<运算符可以将数据写入文件中:

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    ofstream outputFile("output.txt");

    if (!outputFile.is_open()) {
        cerr << "Error: Could not open output file" << endl;
        return 1;
    }

    outputFile << "Hello, World!" << endl;
    outputFile << "This is a line of text." << endl;

    outputFile.close();
    return 0;
}

cpp

3.2 按字符写入文件

使用put方法可以按字符写入文件:

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    ofstream outputFile("output.txt");

    if (!outputFile.is_open()) {
        cerr << "Error: Could not open output file" << endl;
        return 1;
    }

    outputFile.put('H');
    outputFile.put('e');
    outputFile.put('l');
    outputFile.put('l');
    outputFile.put('o');

    outputFile.close();
    return 0;
}

cpp

3.3 写入格式化数据

使用<<运算符可以将格式化的数据写入文件:

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    ofstream outputFile("output.txt");

    if (!outputFile.is_open()) {
        cerr << "Error: Could not open output file" << endl;
        return 1;
    }

    int age = 30;
    double salary = 55000.75;

    outputFile << "Age: " << age << endl;
    outputFile << "Salary: " << salary << endl;

    outputFile.close();
    return 0;
}

cpp

4. 文件流的状态检查

在进行文件操作时,检查文件流的状态非常重要,以确保操作成功进行。常见的文件流状态包括goodfaileofbad状态。

4.1 检查文件流状态

使用good()fail()eof()bad()方法可以检查文件流的状态:

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    ifstream inputFile("input.txt");

    if (inputFile.good()) {
        cout << "File opened successfully" << endl;
    } else if (inputFile.fail()) {
        cerr << "File opening failed" << endl;
    } else if (inputFile.eof()) {
        cerr << "End of file reached" << endl;
    } else if (inputFile.bad()) {
        cerr << "File stream is in a bad state" << endl;
    }

    inputFile.close();
    return 0;
}

cpp

4.2 清除文件流的错误状态

在捕获和处理文件流错误后,可以使用clear()方法重置文件流的状态:

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    ifstream inputFile("input.txt");

    if (!inputFile) {
        cerr << "Error: Could not open input file" << endl;
        return 1;
    }

    // 读取文件内容
    // ...

    // 清除错误状态
    inputFile.clear();
    inputFile.close();
    return 0;
}

cpp

5. 文件操作的最佳实践

在进行文件I/O操作时,遵循一些最佳实践可以提高程序的健壮性和性能。

5.1 确保文件正常关闭

在完成文件操作后,务必关闭文件,以确保文件资源被正确释放。

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    ofstream outputFile("output.txt");

    if (!outputFile.is_open()) {
        cerr << "Error: Could not open output file" << endl;
        return 1;
    }

    // 写入文件内容
    outputFile << "Some text" << endl;

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

    return 0;
}

cpp

5.2 使用RAII管理文件流

RAII(Resource Acquisition Is Initialization)是一种编程惯例,确保资源在对象的生命周期内被正确管理。在C++中,RAII是通过构造函数和析构函数来实现资源的自动管理,确保在对象销毁时释放资源。在文件处理的上下文中,这意味着我们可以利用fstream类来自动管理文件资源。

5.2.1 基本示例

fstream类(包括ifstreamofstream)使用RAII来自动管理文件的打开和关闭。以下是一个简单的例子,展示了如何使用ifstream类读取文件,并依靠RAII机制来确保文件流在完成后自动关闭。

 

cpp

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main() {
    // 使用RAII管理文件流
    ifstream inputFile("example.txt");

    // 检查文件是否成功打开
    if (!inputFile.is_open()) {
        cerr << "Error: Could not open file for reading" << endl;
        return 1;
    }

    string line;
    while (getline(inputFile, line)) {
        // 逐行读取文件内容并输出
        cout << line << endl;
    }

    // 文件流将在此自动关闭(RAII机制)
    return 0;
}

cpp

在这个示例中,ifstream对象inputFile在构造时打开文件,并在inputFile对象离开作用域时自动调用其析构函数,从而关闭文件流。

5.2.2 自定义类实现RAII

我们也可以定义一个自定义类来管理文件流,进一步演示RAII的应用。这对于需要自定义文件处理行为的情况尤其有用。

 

cpp

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

class FileHandler {
private:
    fstream fileStream;

public:
    // 构造函数打开文件
    FileHandler(const string& fileName, ios::openmode mode) {
        fileStream.open(fileName, mode);
        if (!fileStream.is_open()) {
            cerr << "Error: Could not open file" << endl;
            throw runtime_error("File opening failed");
        }
    }

    // 析构函数自动关闭文件
    ~FileHandler() {
        if (fileStream.is_open()) {
            fileStream.close();
        }
    }

    // 提供访问文件流的接口
    fstream& getStream() {
        return fileStream;
    }
};

int main() {
    try {
        FileHandler fileHandler("example.txt", ios::in);
        string line;

        while (getline(fileHandler.getStream(), line)) {
            // 逐行读取文件内容并输出
            cout << line << endl;
        }

        // 文件流将在此自动关闭(RAII机制)
    } catch (const runtime_error& e) {
        cerr << e.what() << endl;
        return 1;
    }

    return 0;
}

cpp

在这个示例中,FileHandler类封装了fstream对象,并通过构造函数打开文件。析构函数确保文件在对象销毁时被正确关闭。使用FileHandler类可以简化文件处理并减少错误。

5.2.3 RAII与异常安全

RAII还帮助我们处理异常情况。由于资源的管理与对象的生命周期绑定,当异常发生时,资源会被自动释放,从而保证程序的异常安全。

 

cpp

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

class SafeFileHandler {
private:
    fstream fileStream;

public:
    SafeFileHandler(const string& fileName, ios::openmode mode) {
        fileStream.open(fileName, mode);
        if (!fileStream.is_open()) {
            throw runtime_error("Failed to open file");
        }
    }

    ~SafeFileHandler() {
        if (fileStream.is_open()) {
            fileStream.close();
        }
    }

    fstream& getStream() {
        return fileStream;
    }
};

int main() {
    try {
        SafeFileHandler fileHandler("example.txt", ios::in);
        string line;

        while (getline(fileHandler.getStream(), line)) {
            // 逐行读取文件内容并输出
            cout << line << endl;
        }
    } catch (const runtime_error& e) {
        cerr << "Error: " << e.what() << endl;
        return 1;
    }

    return 0;
}

cpp

6. 文件的附加操作

在C++中,除了基本的读写操作外,还有一些附加操作可以帮助你更好地管理文件和数据。

6.1 追加写入数据

有时你可能希望向一个已存在的文件中追加数据,而不是覆盖文件内容。可以使用ofstreamopen方法并传递ios::app模式来实现追加操作。

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    ofstream outputFile;
    outputFile.open("output.txt", ios::app);

    if (!outputFile.is_open()) {
        cerr << "Error: Could not open output file" << endl;
        return 1;
    }

    outputFile << "Appending this line to the file." << endl;

    outputFile.close();
    return 0;
}

cpp

6.2 读取文件的当前位置

在读取文件时,可能需要知道当前读取的位置。这可以通过tellg方法获取。

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    ifstream inputFile("input.txt");

    if (!inputFile.is_open()) {
        cerr << "Error: Could not open input file" << endl;
        return 1;
    }

    // 读取部分数据
    string line;
    getline(inputFile, line);

    // 获取当前读取位置
    streampos pos = inputFile.tellg();
    cout << "Current position: " << pos << endl;

    inputFile.close();
    return 0;
}

cpp

6.3 设置文件的读写位置

你可以使用seekgseekp方法来设置文件的读写位置。

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    // 写入文件
    ofstream outputFile("example.txt");
    outputFile << "Hello, World!";
    outputFile.close();

    // 读取文件
    ifstream inputFile("example.txt");

    if (!inputFile.is_open()) {
        cerr << "Error: Could not open input file" << endl;
        return 1;
    }

    // 移动到文件的开始位置
    inputFile.seekg(0);

    // 读取文件内容
    string content;
    getline(inputFile, content);
    cout << "Content: " << content << endl;

    inputFile.close();
    return 0;
}

cpp

7. 二进制文件处理

除了文本文件,C++也支持对二进制文件的操作。二进制文件用于存储非文本数据,如图像、音频或自定义数据格式。处理二进制文件时,需要注意读取和写入数据的方式。

7.1 写入二进制数据

使用ofstreambinary模式可以写入二进制数据。

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    ofstream outputFile("binary.dat", ios::binary);

    if (!outputFile.is_open()) {
        cerr << "Error: Could not open output file" << endl;
        return 1;
    }

    int num = 12345;
    outputFile.write(reinterpret_cast<const char*>(&num), sizeof(num));

    outputFile.close();
    return 0;
}

cpp

7.2 读取二进制数据

读取二进制数据的方式与写入类似,但使用ifstreambinary模式。

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    ifstream inputFile("binary.dat", ios::binary);

    if (!inputFile.is_open()) {
        cerr << "Error: Could not open input file" << endl;
        return 1;
    }

    int num;
    inputFile.read(reinterpret_cast<char*>(&num), sizeof(num));
    cout << "Read number: " << num << endl;

    inputFile.close();
    return 0;
}

cpp

7.3 处理自定义二进制格式

在处理自定义二进制格式时,你需要定义数据结构并确保读写操作与数据结构一致。例如,定义一个自定义结构体并将其写入和读取到二进制文件中。

 

cpp

#include <iostream>
#include <fstream>

using namespace std;

struct Person {
    char name[50];
    int age;
};

int main() {
    // 写入自定义数据结构
    ofstream outputFile("person.dat", ios::binary);

    if (!outputFile.is_open()) {
        cerr << "Error: Could not open output file" << endl;
        return 1;
    }

    Person person = {"John Doe", 30};
    outputFile.write(reinterpret_cast<const char*>(&person), sizeof(person));

    outputFile.close();

    // 读取自定义数据结构
    ifstream inputFile("person.dat", ios::binary);

    if (!inputFile.is_open()) {
        cerr << "Error: Could not open input file" << endl;
        return 1;
    }

    Person readPerson;
    inputFile.read(reinterpret_cast<char*>(&readPerson), sizeof(readPerson));
    cout << "Name: " << readPerson.name << ", Age: " << readPerson.age << endl;

    inputFile.close();
    return 0;
}

cpp

8. 文件的同步和异步操作

文件操作通常是同步的,即操作会阻塞直到完成。然而,在处理大文件或需要高性能时,可以使用异步操作来避免阻塞主线程。

8.1 同步文件操作

同步文件操作是默认的文件操作方式,在文件操作完成前,程序会等待。

 

cpp

#include <iostream>
#include <fstream>
#include <thread>

using namespace std;

void writeFile() {
    ofstream outputFile("sync.txt");

    if (!outputFile.is_open()) {
        cerr << "Error: Could not open output file" << endl;
        return;
    }

    outputFile << "This is a synchronous write operation." << endl;

    outputFile.close();
}

int main() {
    writeFile();
    cout << "File written synchronously." << endl;
    return 0;
}

cpp

8.2 异步文件操作

异步文件操作可以使用线程来实现,将文件操作放在一个独立的线程中,以避免主线程阻塞。

 

cpp

#include <iostream>
#include <fstream>
#include <thread>

using namespace std;

void writeFileAsync() {
    ofstream outputFile("async.txt");

    if (!outputFile.is_open()) {
        cerr << "Error: Could not open output file" << endl;
        return;
    }

    outputFile << "This is an asynchronous write operation." << endl;

    outputFile.close();
}

int main() {
    thread t(writeFileAsync);
    t.join(); // 等待线程完成
    cout << "File written asynchronously." << endl;
    return 0;
}

cpp

9. 总结

本文详细介绍了C++中关于文件I/O操作的各个方面,包括文件的基本读写操作、附加操作、二进制文件处理以及同步与异步文件操作。掌握这些技能可以帮助你更有效地进行文件管理和数据处理。在下一篇教程中,我们将探讨C++中的多线程编程,学习如何使用线程来实现并发操作,提高程序的性能和响应能力。

 

 一、ASCII 输出   为了使用下面的方法, 你必须包含头文件(译者注:在标准C++中,已经使用取 代,所有的C++标准头文件都是无后缀的。)。这是 的一个扩展集, 提供有缓 冲的文件输入输出操作. 事实上, 已经被包含了, 所以你不必包含所有这两个 文件, 如果你想显式包含他们,那随便你。我们从文件操作类的设计开始, 我会讲解如何进行ASCII I/O 操作。如果你猜是"fstream," 恭喜你答对了! 但这篇文章介绍的方法,我们分别使用"ifstream"?和 "ofstream" 来作输入输出。   如果你用过标准控制台流"cin"?和 "cout," 那现在的事情对你来说很简单。 我们现在开始讲输出部 分,首先声明一个类对象。 ofstream fout;   这就可以了,不过你要打开一个文件的话, 必须像这样调用ofstream::open()。 fout.open("output.txt");   你也可以把文件名作为构造参数来打开一个文件. ofstream fout("output.txt");   这是我们使用的方法, 因为这样创建和打开一个文件看起来更简单. 顺便说一句, 如果你要打开的文 件不存在,它会为你创建一个, 所以不用担心文件创建的问题. 现在就输出到文件,看起来和"cout"的操 作很像。 对不了解控制台输出"cout"的人, 这里有个例子。 int num = 150; char name[] = "John Doe"; fout << "Here is a number: " << num << " "; fout << "Now here is a string: " << name << " ";   现在保存文件,你必须关闭文件,或者回写文件缓冲. 文件关闭之后就不能再操作了, 所以只有在你 不再操作这个文件的时候才调用它,它会自动保存文件。 回写缓冲区会在保持文件打开的情况下保存文 件, 所以只要有必要就使用它。回写看起来像另一次输出, 然后调用方法关闭。像这样: fout << flush; fout.close();    现在你用文本编辑器打开文件,内容看起来是这样:   Here is a number: 150 Now here is a string: John Doe   很简单吧! 现在继续文件输入, 需要一点技巧, 所以先确认你已经明白了流操作,对 "<>" 比较熟悉了, 因为你接下来还要用到他们。继续…   二、ASCII 输入   输入和"cin" 流很像. 和刚刚讨论的输出流很像, 但你要考虑几件事情。在我们开始复杂的内容之前 , 先看一个文本:   12 GameDev 15.45 L This is really awesome!   为了打开这个文件,你必须创建一个in-stream对象,?像这样。 ifstream fin("input.txt");   现在读入前四行. 你还记得怎么用"<<" 操作符往流里插入变量和符号吧?好,?在 "<>" (提取) 操作符. 使用方法是一样的. 看这个代码片段.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值