Cpp输入输出流

Cpp通过以下几个类支持文件的输入输出:

  • ofstream:写操作(输出)的文件类 (由ostream引申而来)
  • ifstream:读操作(输入)的文件类(由istream引申而来)
  • fstream:可同时读写操作的文件类 (由iostream引申而来)

打开文件(Open a file)

对这些类的一个对象所做的第一个操作通常就是将它和一个真正的文件联系起来,也就是说打开一个文件。被打开的文件在程序中由一个流对象(stream object)来表示 (这些类的一个实例) ,而对这个流对象所做的任何输入输出操作实际就是对该文件所做的操作。

要通过一个流对象打开一个文件,我们使用它的成员函数open()

void open (const char * filename, openmode mode);

这里filename 是一个字符串,代表要打开的文件名,mode 是以下标志符的一个组合:

ios::in //为输入(读)而打开文件
ios::out //为输出(写)而打开文件
ios::ate //初始位置:文件尾
ios::app //所有输出附加在文件末尾
ios::trunc //如果文件已存在则先删除该文件
ios::binary //二进制方式

这些标识符可以被组合使用,中间以“或”操作符|间隔。例如,如果我们想要以二进制方式打开文件example.bin来写入一些数据,我们可以通过以下方式调用成员函数open()来实现:

ofstream file;
file.open("example.bin", ios::out | ios::app | ios::binary);

ofstreamifstreamfstream所有这些类的成员函数open 都包含了一个默认打开文件的方式,这三个类的默认方式各不相同:

类 参数的默认方式
ofstream ios::out | ios::trunc
ifstream ios::in
fstream ios::in | ios::out

只有当函数被调用时没有声明方式参数的情况下,默认值才会被采用。如果函数被调用时声明了任何参数,默认值将被完全改写,而不会与调用参数组合。

由于对类ofstreamifstreamfstream的对象所进行的第一个操作通常都是打开文件,这些类都有一个构造函数可以直接调用open函数,并拥有同样的参数。这样,我们就可以通过以下方式进行与上面同样的定义对象和打开文件的操作:

ofstream file ("example.bin", ios::out | ios::app | ios::binary);

两种打开文件的方式都是正确的。

你可以通过调用成员函数is_open()来检查一个文件是否已经被顺利的打开了:

bool is_open();

它返回一个布尔值,为真代表文件已经被顺利打开,假则相反。

关闭文件(Closing a file)

当文件读写操作完成之后,我们必须将文件关闭以使文件重新变为可访问的。关闭文件需要调用成员函数close(),它负责将缓存中的数据排放出来并关闭文件。它的格式很简单:

void close ();

这个函数一旦被调用,原先的流对象(stream object)就可以被用来打开其它的文件了,这个文件也就可以重新被其它的进程(process)所有访问了。

为防止流对象被销毁时还联系着打开的文件,析构函数(destructor)将会自动调用关闭函数close()

文本文件(Text mode files)

ofstreamifstreamfstream是分别从ofstreamifstreamfstream中引申而来的。这就是为什么 fstream 的对象可以使用其父类的成员来访问数据。

一般来说,我们将使用这些类与同控制台(console)交互同样的成员函数(cin and cout)来进行输入输出。如下面的例题所示,我们使用重载的插入操作符:<<

Example: writing on a text file

#include <fiostream.h>

int main () {
    ofstream examplefile ("example.txt");
    if (examplefile.is_open()) {
        examplefile << "This is a line.\n";
        examplefile << "This is another line.\n";
        examplefile.close();
    }
    return 0;
}

file example.txt:

This is a line.
This is another line.

从文件中读入数据也可以用与 cin的使用同样的方法:

Example: reading a text file

#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>

int main () {
    char buffer[256];
    ifstream examplefile ("example.txt");
    if (! examplefile.is_open()) {
        cout << "Error opening file";
        exit(1);
    }
    while (! examplefile.eof()) {
        examplefile.getline (buffer,100);
        cout << buffer << endl;
    }

    return 0;
}

Console:

This is a line.
This is another line.  

上面的例子读入一个文本文件的内容,然后将它打印到屏幕上。注意我们使用了一个新的成员函数叫做eof(),它是ifstream从类ios中继承过来的,当到达文件末尾时返回true

状态标志符的验证(Verification of state flags)

除了eof()以外,还有一些验证流的状态的成员函数(所有都返回bool型返回值):

  • bad():如果在读写过程中出错,返回true。例如:当我们要对一个不是打开为写状态的文件进行写入时,或者我们要写入的设备没有剩余空间的时候。

  • fail(): 除了与bad()同样的情况下会返回 true 以外,加上格式错误时也返回true,例如当想要读入一个整数,而获得了一个字母的时候。

  • eof(): 如果读文件到达文件末尾,返回true

  • good(): 这是最通用的:如果调用以上任何一个函数返回true的话,此函数返回false

要想重置以上成员函数所检查的状态标志,你可以使用成员函数clear(),没有参数。

获得和设置流指针(get and put stream pointers)

所有输入/输出流对象(i/o streams objects)都有至少一个流指针:

  • ifstream:类似istream,有一个被称为get pointer的指针,指向下一个将被读取的元素。
  • ofstream:类似 ostream,有一个指针put pointer,指向写入下一个元素的位置。
  • fstream:类似iostream同时继承了getput

我们可以通过使用以下成员函数来读出或配置这些指向流中读写位置的流指针:

  • tellg() & tellp()

这两个成员函数不用传入参数,返回pos_type类型的值(ANSI-C++),就是一个整数,代表当前get流指针的位置tellg()put流指针的位置tellp().

  • seekg() & seekp()

这对函数分别用来改变流指针getput的位置。两个函数都被重载为两种不同的原型:

seekg(pos_type position);
seekp(pos_type position);

使用这个原型,流指针被改变为指向从文件开始计算的一个绝对位置。要求传入的参数类型与函数tellgtellp的返回值类型相同。

seekg(off_type offset, seekdir direction);
seekp(off_type offset, seekdir direction);

使用这个原型可以指定由参数direction决定的一个具体的指针开始计算的一个位移(offset)。它可以是:

 ios::beg //从流开始位置计算的位移
 ios::cur //从流指针当前位置开始计算的位移
 ios::end //从流末尾处开始计算的位移

流指针getput的值对文本文件(text file)和二进制文件(binary file)的计算方法都是不同的,因为文本模式的文件中某些特殊字符可能被修改。由于这个原因,建议对以文本文件模式打开的文件总是使用seekgseekp的第一种原型,而且不要对tellgtellp的返回值进行修改。对二进制文件,你可以任意使用这些函数,应该不会有任何意外的行为产生。

以下例子使用这些函数来获得一个二进制文件的大小:

Example: obtaining file size

#include <iostream.h>
#include <fstream.h>

const char * filename = "example.txt";

int main () {
    long l,m;
    ifstream file (filename, ios::in|ios::binary);
    l = file.tellg();
    file.seekg (0, ios::end);
    m = file.tellg();
    file.close();
    cout << "size of " << filename;
    cout << " is " << (m-l) << " bytes.\n";
    return 0;
}

二进制文件(Binary files)

在二进制文件中,使用<<>>,以及函数(如getline)来操作符输入和输出数据,没有什么实际意义,虽然它们是符合语法的。

文件流包括两个为顺序读写数据特殊设计的成员函数:write()read()。第一个函数write()ostream的一个成员函数,都是被ofstream所继承。而read()istream的一个成员函数,被ifstream所继承。类fstream的对象同时拥有这两个函数。它们的原型是:

write(char * buffer, streamsize size);
read(char * buffer, streamsize size);

这里buffer是一块内存的地址,用来存储或读出数据。参数size 是一个整数值,表示要从缓存(buffer)中读出或写入的字符数。

Example: reading binary file

#include <iostream>
#include <fstream.h>

const char * filename = "example.txt";

int main () {
    char * buffer;
    long size;
    ifstream file (filename, ios::in|ios::binary|ios::ate);
    size = file.tellg();
    file.seekg (0, ios::beg);
    buffer = new char [size];
    file.read (buffer, size);
    file.close();
    cout << "the complete file is in a buffer";
    delete[] buffer;

    return 0;
}

Console:

The complete file is in a buffer

总结

### C++ 输入输出流 (IO Stream) 性能优化技巧 在C++编程中,输入输出流(IO Streams)是一种强大的工具,用于处理程序内外的数据交换。然而,默认情况下,这些操作可能不是最高效的。以下是几种常见的性能优化方法: #### 1. 解耦 `cin` 和 `stdio` 默认情况下,C++的 `std::cin` 和 C 风格的标准输入函数(如 `scanf` 或 `getchar`)是同步的。这意味着它们共享同一个缓冲区,这会降低效率。可以通过调用以下语句禁用这种同步行为: ```cpp std::ios::sync_with_stdio(false); ``` 此设置可以显著提高基于流的操作的速度[^2]。 #### 2. 关闭流的自动刷新功能 对于某些流对象(例如 `std::cerr`),每次写入都会触发一次强制刷新到终端的行为。如果不需要实时显示日志,则应避免频繁刷新缓存。通过调整代码逻辑减少不必要的刷新次数能够提升整体吞吐量[^5]。 #### 3. 使用固定大小的缓冲区 当执行大量连续读取或写入时,手动指定较大的内部缓冲区有助于减少磁盘I/O频率并加快速度。下面是一个例子展示如何为文件流分配更大的缓冲空间: ```cpp const int BUFFER_SIZE = 1024 * 1024; // 设置为1MB char buffer[BUFFER_SIZE]; std::ifstream file; file.rdbuf()->pubsetbuf(buffer, sizeof(buffer)); file.open("example.txt"); if (!file.is_open()) { std::cerr << "Failed to open file!" << std::endl; } // 继续正常读取... ``` #### 4. 替代方案——考虑使用更快的方式代替标准库实现 尽管STL提供了丰富的接口支持各种场景下的数据传输需求,但在追求极致效能的情况下,可以直接采用底层API或者第三方高效框架完成相应任务。比如利用POSIX系统调用来替代高级别的封装版本可能会带来更好的表现效果[^4]。 #### 5. 减少格式化字符串带来的开销 过度依赖复杂的格式控制符也会拖慢运行时间。尽量简化表达方式,并且只在必要时候才启用额外特性(像精度设定、宽度填充等功能)[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值