由一个Bug看ifstream中read()与readsome()的区别

项目中读取特定二进制文件时,数据量超400K左右程序出错,最终发现是使用fsream::readsome()函数所致,改成fsream::read()即可。详细介绍了流的派生关系,指出read()和readsome()区别在于是否迫使同步缓冲区,还提及选择readsome()的原因。

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

 

ifstream中read()与readsome()的区别。

项目中出现一个很有趣的Bug,程序读入一个特定的二进制文件中的数据,进行一些处理,可是每当这个文件的数据量超过某个特定值(这个值不大,400K左右)的时候运行结果就出错。

追查Bug是程序员的一项基本功,也是最能体现实力的地方之一。这个Bug最终在3天追捕之后被一个同事抓获。原因确是因为我写的读取文件的模块中使用了fsream::readsome()函数。改成fsream::read()就可以了。

惊悸之余,特地比较详细的看了关于流的解释。

首先,fstream是basic_fstream模板类关于char的一个实例。

typedef basic_fstream<char, char_traits<char> > fstream;

basic_fstream的由basic_iostream派生的

template <class Elem, class Tr = char_traits<Elem> >
    class basic_fstream : public basic_iostream<Elem, Tr>

basic_iostream是由basic_istream和basic_ostream派生的。

template <class Elem, class Tr = char_traits<Elem> >
   class basic_iostream : public basic_istream<Elem, Tr>,
      public basic_ostream<Elem, Tr> 

我们使用的fstream::readsome实际上是 basic_istream::readsome().

而basic_istream是从basic_ios派生的

template <class Elem, class Tr = char_traits<Elem> >
   class basic_istream
      : virtual public basic_ios<Elem, Tr>

basic_ios是从ios_base派生的

template <class Elem, class Traits>
   class basic_ios : public ios_base
派生关系图如下:

 

在流的实现上,采取了缓冲区I/O的方式,每个stream对象中都有一个对于一个streambuf,对于流的操作缺省状态下是对于缓冲区的操作,直到出现迫使他和缓冲区同步的操作。

而read()和readsome()的区别就在于此,readsome()并不迫使同步缓冲区,而read()读取的时候,如果发现缓冲区中的数据不够,就试图从关联的数据源(这里就是文件)来读取。

readsome()函数之后调用gcount()可以检测究竟有多少字节被操作了。good()函数也可以检测,而我没有做。

 

我当初选择readsome()是以为read()是操作charactor的,而readsome()才是操作二进制的。

 

 

### C++ 中 `ifstream::read` 函数的用法 在C++中,`ifstream::read` 是用于从输入文件流读取原始数据的一种方法。此函数允许指定要读取的数据量以及存储这些数据的位置。 下面是一个具体的例子来展示如何使用 `ifstream::read` 方法: ```cpp #include <iostream> #include <fstream> using namespace std; int main() { const size_t bufferSize = 100; char buffer[bufferSize]; ifstream inputFile("example.bin", ios::binary); if (inputFile.is_open()) { // 尝试读取最多 bufferSize - 1 字节到缓冲区 inputFile.read(buffer, bufferSize - 1); // 设置字符串结尾符以便安全打印 buffer[inputFile.gcount()] = '\0'; cout << "Read bytes count: " << inputFile.gcount() << endl; cout << "Data read from file:" << endl; cout.write(buffer, inputFile.gcount()); inputFile.close(); } else { cerr << "Unable to open file"; } return 0; } ``` 上述程序尝试打开名为 `"example.bin"` 的二进制文件并从中读取多达99字节的内容至字符数组 `buffer` 中[^1]。注意这里预留了一个额外的空间给终止零(`\0`)以确保可以作为C风格字符串处理而不会越界访问内存。通过调用成员函数 `gcount()` 可获得实际被读入的数量,并据此设置结束标志位。 #### 关键点说明 - 使用 `ios::binary` 打开模式是为了防止某些平台对换行符做特殊处理。 - 调用 `read` 后应检查返回的状态码或者利用 `gcount()` 来确认成功读取了多少个字节。 - 当操作可能涉及未初始化或超出范围的风险时,在适当位置加入边界条件判断是非常重要的实践之一。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值