C/C++学习笔记十二 Input and Output (I/O)(5)

本文详细介绍了C++中的文件输入输出操作,包括使用ifstream、ofstream和fstream类进行文件读写,以及如何处理文件模式、缓冲输出和错误处理。示例代码展示了如何打开、写入、读取文件,以及如何追加内容到已有文件。强调了缓冲区管理和文件模式的重要性,特别是缓冲区在程序异常终止时可能导致数据丢失的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、基本文件 I/O

        C++ 中的文件 I/O 的工作方式与普通 I/O 非常相似(稍微增加了一些复杂性)。 C++ 中有 3 个基本的文件 I/O 类:ifstream(派生自 istream)、ofstream(派生自 ostream)和 fstream(派生自 iostream)。 这些类分别进行文件输入、输出和输入/输出。 要使用文件 I/O 类,您需要包含 fstream 标头。

        与已经准备好使用的 cout、cin、cerr 和 clog 流不同,文件流必须由程序员明确设置。 然而,这非常简单:要打开一个文件进行读取和/或写入,只需实例化相应文件 I/O 类的对象,并将文件名作为参数。 然后使用插入 (<<) 或提取 (>>) 运算符写入文件或从文件中读取数据。 完成后,有几种方法可以关闭文件:显式调用 close() 函数,或者只是让文件 I/O 变量超出范围(文件 I/O 类析构函数将为您关闭文件) .

1、File output

        为了在以下示例中进行文件输出,我们将使用 ofstream 类。

#include <fstream>
#include <iostream>

int main()
{
    // ofstream is used for writing files
    // We'll make a file called Sample.txt
    std::ofstream outf{ "Sample.txt" };

    // If we couldn't open the output file stream for writing
    if (!outf)
    {
        // Print an error and exit
        std::cerr << "Uh oh, Sample.txt could not be opened for writing!\n";
        return 1;
    }

    // We'll write two lines into this file
    outf << "This is line 1" << '\n';
    outf << "This is line 2" << '\n';

    return 0;

    // When outf goes out of scope, the ofstream
    // destructor will close the file
}

        如果您查看您的项目目录,您应该会看到一个名为 Sample.txt 的文件。 如果你用文本编辑器打开它,你会看到它确实包含我们写入文件的两行。

        请注意,也可以使用 put() 函数将单个字符写入文件。

2、File input

        现在,我们将获取我们在上一个示例中编写的文件并从磁盘读回它。 请注意,如果我们已到达文件末尾 (EOF),ifstream 将返回 0。 我们将使用这个事实来确定要阅读多少。

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

int main()
{
    // ifstream is used for reading files
    // We'll read from a file called Sample.txt
    std::ifstream inf{ "Sample.txt" };

    // If we couldn't open the output file stream for reading
    if (!inf)
    {
        // Print an error and exit
        std::cerr << "Uh oh, Sample.txtcould not be opened for reading!\n";
        return 1;
    }

    // While there's still stuff left to read
    while (inf)
    {
        // read stuff from the file into a string and print it
        std::string strInput;
        inf >> strInput;
        std::cout << strInput << '\n';
    }

    return 0;

    // When inf goes out of scope, the ifstream
    // destructor will close the file
}

        输出如下

This
is
line
1
This
is
line
2

        这不是我们想要的。 请记住,提取运算符会在空格处中断。为了读取整行,我们必须使用 getline() 函数。

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

int main()
{
    // ifstream is used for reading files
    // We'll read from a file called Sample.txt
    std::ifstream inf{ "Sample.txt" };

    // If we couldn't open the input file stream for reading
    if (!inf)
    {
        // Print an error and exit
        std::cerr << "Uh oh, Sample.txt could not be opened for reading!\n";
        return 1;
    }

    // While there's still stuff left to read
    while (inf)
    {
        // read stuff from the file into a string and print it
        std::string strInput;
        std::getline(inf, strInput);
        std::cout << strInput << '\n';
    }

    return 0;

    // When inf goes out of scope, the ifstream
    // destructor will close the file
}

        输出如下

This is line 1
This is line 2

3、Buffered output

        C++ 中的输出可能会被缓冲。 这意味着输出到文件流的任何内容都可能不会立即写入磁盘。 相反,可以将多个输出操作一起批处理和处理。 这样做主要是出于性能原因。 当缓冲区写入磁盘时,这称为刷新缓冲区。 导致缓冲区被刷新的一种方法是关闭文件——缓冲区的内容将被刷新到磁盘,然后文件将被关闭。

        缓冲通常不是问题,但在某些情况下,它可能会给粗心的人带来麻烦。 在这种情况下,罪魁祸首是缓冲区中有数据,然后程序立即终止(通过崩溃或调用 exit())。 在这些情况下,文件流类的析构函数不会被执行,这意味着文件永远不会关闭,这意味着缓冲区永远不会被刷新。 在这种情况下,缓冲区中的数据不会写入磁盘,并且会永远丢失。 这就是为什么在调用 exit() 之前显式关闭所有打开的文件总是一个好主意的原因。

        可以使用 ostream::flush() 函数手动刷新缓冲区或将 std::flush 发送到输出流。 这些方法中的任何一种都有助于确保缓冲区的内容立即写入磁盘,以防程序崩溃。

        一个有趣的注意事项是 std::endl; 还刷新输出流。 因此,过度使用 std::endl(导致不必要的缓冲区刷新)可能会在执行刷新代价高昂的缓冲 I/O(例如写入文件)时对性能产生影响。 出于这个原因,注重性能的程序员通常会使用“\n”而不是 std::endl 在输出流中插入换行符,以避免不必要的缓冲区刷新。

4、File modes

        如果我们尝试写入一个已经存在的文件会发生什么? 再次运行输出示例显示每次运行程序时都完全覆盖了原始文件。 相反,如果我们想在文件末尾附加更多数据怎么办? 事实证明,文件流构造函数采用可选的第二个参数,允许您指定有关如何打开文件的信息。 此参数称为模式,它接受的有效标志位于 Ios 类中。

         可以通过按位或将它们一起指定多个标志(使用 | 运算符)。 Ifstream 默认为 std::ios::in 文件模式。 Ofstream 默认为 std::ios::out 文件模式。 fstream 默认为 std::ios::in | std::ios::out 文件模式,这意味着默认情况下您可以读写。

        由于 fstream 的设计方式,如果使用 std::ios::in 并且打开的文件不存在,它可能会失败。 如果您需要使用 fstream 创建新文件,请仅使用 std::ios::out 模式。

        让我们编写一个程序,在我们之前创建的 Sample.txt 文件中再添加两行:

#include <iostream>
#include <fstream>

int main()
{
    // We'll pass the ios:app flag to tell the ofstream to append
    // rather than rewrite the file.  We do not need to pass in std::ios::out
    // because ofstream defaults to std::ios::out
    std::ofstream outf{ "Sample.txt", std::ios::app };

    // If we couldn't open the output file stream for writing
    if (!outf)
    {
        // Print an error and exit
        std::cerr << "Uh oh, Sample.txt could not be opened for writing!\n";
        return 1;
    }

    outf << "This is line 3" << '\n';
    outf << "This is line 4" << '\n';

    return 0;

    // When outf goes out of scope, the ofstream
    // destructor will close the file
}

        现在,如果我们查看 Sample.txt(使用上述示例程序之一打印其内容,或将其加载到文本编辑器中),我们将看到以下内容:

This is line 1
This is line 2
This is line 3
This is line 4

5、使用 open() 显式打开文件

        就像可以使用 close() 显式关闭文件流一样,也可以使用 open() 显式打开文件流。 open() 就像文件流构造函数一样工作——它需要一个文件名和一个可选的文件模式。

std::ofstream outf{ "Sample.txt" };
outf << "This is line 1" << '\n';
outf << "This is line 2" << '\n';
outf.close(); // explicitly close the file

// Oops, we forgot something
outf.open("Sample.txt", std::ios::app);
outf << "This is line 3\n";
outf.close();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坐望云起

如果觉得有用,请不吝打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值