我目前正在学习 C + + (来自 Java) ,并试图理解如何在 C + + 中正确地使用 IO 流。
假设我有一个包含图像像素的 Image
类,我重载了提取操作符来从流中读取图像:
istream& operator>>(istream& stream, Image& image)
{
// Read the image data from the stream into the image
return stream;
}
现在我可以读出这样的图像:
Image image;
ifstream file("somepic.img");
file >> image;
但是现在我想使用相同的提取操作符从自定义流中读取图像数据。假设我有一个文件,其中包含压缩形式的图像。因此,我可能想实现自己的输入流,而不是使用 ifstream。至少我在 Java 里是这么做的。在 Java 中,我将编写一个自定义类来扩展 InputStream
类并实现 int read()
方法。这很简单。使用方法如下:
InputStream stream = new CompressedInputStream(new FileInputStream("somepic.imgz"));
image.read(stream);
因此,使用相同的模式,也许我想在 C + + 中这样做:
Image image;
ifstream file("somepic.imgz");
compressed_stream stream(file);
stream >> image;
但也许这是错误的方式,不知道。扩展 istream
类看起来相当复杂,在一些搜索之后,我发现了一些关于扩展 streambuf
的提示。但是对于这样一个简单的任务,这个 例子看起来非常复杂。
那么,在 C + + 中实现自定义输入/输出流(或者 Strebufs)的最佳方式是什么呢?
一些人建议根本不要使用 iostream,而是使用迭代器、升级或自定义 IO 接口。这些可能是有效的选择,但我的问题是关于 iostream。接受的答案导致了下面的示例代码。为了更容易阅读,没有标题/代码分离,并且导入了整个 std 名称空间(我知道在实际代码中这是一件坏事)。
此示例是关于读取和写入垂直 xor 编码的图像的。格式很简单。每个字节表示两个像素(每个像素4位)。每一行都与前一行相异。这种编码为压缩图像做准备(通常会产生大量的0字节,这样更容易压缩)。
#include <cstring>
#include <fstream>
using namespace std;
/*** vxor_streambuf class ******************************************/
class vxor_streambuf: public streambuf
{
public:
vxor_streambuf(streambuf *buffer, const int width) :
buffer(buffer),
size(width / 2)
{
previous_line = new char[size];
memset(previous_line, 0, size);
current_line = new char[size];
setg(0, 0, 0);
setp(current_line, current_line + size);
}
virtual ~vxor_streambuf()
{
sync();
delete[] previous_line;
delete[] current_line;
}
virtual streambuf::int_type underflow()
{
// Read line from original buffer
streamsize read = buffer->sgetn(current_line, size);
if (!read) return traits_type::eof();
// Do vertical XOR decoding
for (int i = 0; i < size; i += 1)
{
current_line[i] ^= previous_line[i];
previous_line[i] = current_line[i];
}
setg(current_line, current_line, current_line + read);
return traits_type::to_int_type(*gptr());
}
virtual streambuf::int_type overflow(streambuf::int_type value)
{
int write = pptr() - pbase();
if (write)
{
// Do vertical XOR encoding
for (int i = 0; i < size; i += 1)
{
char tmp = current_line[i];
current_line[i] ^= previous_line[i];
previous_line[i] = tmp;
}
// Write line to original buffer
streamsize written = buffer->sputn(current_line, write);
if (written != write) return traits_type::eof();
}
setp(current_line, current_line + size);
if (!traits_type::eq_int_type(value, traits_type::eof())) sputc(value);
return traits_type::not_eof(value);
};
virtual int sync()
{
streambuf::int_type result = this->overflow(traits_type::eof());
buffer->pubsync();
return traits_type::eq_int_type(result, traits_type::eof()) ? -1 : 0;
}
private:
streambuf *buffer;
int size;
char *previous_line;
char *current_line;
};
/*** vxor_istream class ********************************************/
class vxor_istream: public istream
{
public:
vxor_istream(istream &stream, const int width) :
istream(new vxor_streambuf(stream.rdbuf(), width)) {}
virtual ~vxor_istream()
{
delete rdbuf();
}
};
/*** vxor_ostream class ********************************************/
class vxor_ostream: public ostream
{
public:
vxor_ostream(ostream &stream, const int width) :
ostream(new vxor_streambuf(stream.rdbuf(), width)) {}
virtual ~vxor_ostream()
{
delete rdbuf();
}
};
/*** Test main method **********************************************/
int main()
{
// Read data
ifstream infile("test.img");
vxor_istream in(infile, 288);
char data[144 * 128];
in.read(data, 144 * 128);
infile.close();
// Write data
ofstream outfile("test2.img");
vxor_ostream out(outfile, 288);
out.write(data, 144 * 128);
out.flush();
outfile.close();
return 0;
}