偶然发现一个用了STL很久都没有发现的问题,特记之。
事情的起因很简单,需要做一些简单的重构,将原来读文件得到二进制缓冲数据的部分分离开,拉一个单独的API,接收文件二进制内容的缓冲区参数;估计很多人都会做这样的事情,将一个小程序变得更有用,不得不把300行的main拆开来,露几个函数来用。
其他的部分都如想象般的简单,唯独在测试原有读文件调用新接口的地方卡住了。
原来的逻辑如此这般:
FILE* fin = open("file.bin", "rb");
if (fin == NULL)
{
exit(-1);
}
unsigned char buf[65536] = {0};
char ch;
int i = 0;
while ( (ch = fgetc(fin)) != EOF)
{
buf[i++] = ch;
if (i >= sizeof(buf))
exit(-2);
}
//..................
改写后的调用接口如下:
typedef std::vector<unsigned char> BinaryBufferType;
int SomeFunc(BinaryBufferType& buf, ...)
为了完成接口测试并且还原原有功能,需要将文件内容读入到一个vector中来测试。初始的想法如下:
ifstream ifs("file.bin", ios::in|ios::binary);
if (!ifs.good())
{
exit(-1);
}
typedef std::istream_iterator<char> FsIt;
BinaryBufferType buf;
std::copy(FsIt(ifs), FsIt(), std::back_inserter(buf));
除了出错检查,重要的部分就是一个copy调用将STL流的内容自动拷贝到vector里边;这是一个很典型的例子,乃至SGI的文档里边关于copy算法的例子就是这样的。
问题是,这个代码却是有问题的,和上边的C代码并不等价,实际测试的过程中,发现居然漏掉了3个Byte的数据。
顿时感觉很奇怪了,马上GDB跟了下,由于数据太多,一下子没看出来那个出书丢了(后来发现是0c);想想是否与binary方式有关呢,已经采用binary方式读入了呀?
Google一番才发现有人遇到了同样的问题,原来 stream_iterator 默认采用的是formatted I/O方式处理数据,所以某些东西会被跳过。
如果需要拷贝二进制数据,该采用如下的法子:
typedef std::istreambuf_iterator<char> FsIt;
BinaryBufferType buf;
std::copy(FsIt(buf.rdbuf()), FsIt(), std::back_inserter(buf));
就是这点小小的差别,以前一直被忽略了…… 浪费了不少时间,当时如果搜索istreambuf_iterator,似乎能发现Effective STL里边讲述过这个,可惜当时看的时候,很快过去,
居然一点印象都没有?
真是“绝知此事要躬行”了。