以某种方式缓冲或包装 cin 以便我可以使用 tellg/seekg?
Someway to buffer or wrap cin so I can use tellg/seekg?
有什么方法可以为 cin 添加缓冲,以便我可以在该 istream 上有效地使用 tellg 和 seekg 吗? (我只需要返回大约 6 个字符。)或者是否有某种方法可以用(可能是自定义的)istream object 来包装流,它充当缓冲管道,允许我使用 tellg/seekg 恢复流位置几个字符?它可能看起来像这样:
BufferedIStream bis(cin);
streampos pos = bis.tellg();
MyObjectType t = getObjectType(bis);
bis.seekg(pos);
作为 work-around,我目前正在将 cin 读入 EOF 到一个字符串中,并将该字符串传输到一个 istringstream,但这有许多我想避免的负面 side-affects .
我唯一能想到的另一件事是使用私有版本(仅供工厂使用)在我所有数据 类 上重载我所有 scan/read 函数,其中 header 假设已经被消费了,所以我可以完全消除对 tellg/seekg 的需要。这会工作得很好,但会引入 fair-amount 的丑陋。相比之下, tellg/seekg 与我的工厂隔离,只是两行代码。我讨厌把它扔掉。
您可以创建一个过滤流缓冲区,即从 std::streambuf
派生的 class。为了支持缓冲读取,您将覆盖 underflow()
以在输入字符被消耗后填充下一个字符缓冲区。为了支持有限搜索,先前的缓冲区不会被丢弃,而是部分保留。此外,您将覆盖 seekoff()
.
像这样应该可以解决问题:
#include <iostream>
#include <streambuf>
#include <string>
#include <cstdlib>
#include <cstring>
class bufferbuf
: public std::streambuf {
enum { size = 2000, half = size / 2 };
char buffer[size];
std::streambuf* sbuf;
std::streamoff base;
public:
bufferbuf(std::streambuf* sbuf): sbuf(sbuf), base() {
auto read = sbuf->sgetn(this->buffer, size);
this->setg(this->buffer, this->buffer, this->buffer + read);
}
int underflow() {
if (this->gptr() == this->buffer + size) {
std::memmove(this->eback(), this->eback() + half, half);
base += half;
auto read = sbuf->sgetn(this->eback() + half, half);
this->setg(this->eback(), this->eback() + half, this->eback() + half + read);
}
return this->gptr() != this->egptr()
? traits_type::to_int_type(*this->gptr())
: traits_type::eof();
}
std::streampos seekoff(off_type offset,
std::ios_base::seekdir whence,
std::ios_base::openmode which) override {
if (this->gptr() - this->eback() < -offset
|| this->egptr() - this->gptr() < offset
|| whence != std::ios_base::cur
|| !(which & std::ios_base::in)) {
return pos_type(off_type(-1));
}
this->gbump(offset);
return pos_type(this->base + (this->gptr() - this->eback()));
}
std::streampos seekpos(pos_type pos, std::ios_base::openmode which) override {
if (off_type(pos) < this->base
|| this->base + (this->egptr() - this->eback()) < off_type(pos)
|| !(which & std::ios_base::in)) {
return pos_type(off_type(-1));
}
this->setg(this->eback(), this->eback() + (off_type(pos) - this->base), this->egptr());
return pos_type(base + (this->gptr() - this->eback()));
}
};
int main() {
bufferbuf buf(std::cin.rdbuf());
std::istream in(&buf);
// ...
std::string s0, s1;
bool relative(false);
if (relative) {
while (in >> s0
&& (in.seekg(-int(s0.size()), std::ios_base::cur), in >> s1)) {
std::cout << "read "
<< "s0='" << s0 << "' " << "s1='" << s1 << "'\n";
}
}
else {
for (std::streampos pos = in.tellg();
in >> s0 && (in.seekg(pos), in >> s1); pos = in.tellg()) {
std::cout << "read "
<< "s0='" << s0 << "' " << "s1='" << s1 << "'\n";
}
}
}
上面的代码适用于几个简单的测试用例。它演示了相对定位和绝对定位的使用。一般来说,我发现在流中寻找是没有用的,因为通常每个有趣的词法分析都可以只用一个字符前瞻来完成。结果,我可能错过了位置方面的一些东西。但是,我希望上面的代码能够正常工作。
有什么方法可以为 cin 添加缓冲,以便我可以在该 istream 上有效地使用 tellg 和 seekg 吗? (我只需要返回大约 6 个字符。)或者是否有某种方法可以用(可能是自定义的)istream object 来包装流,它充当缓冲管道,允许我使用 tellg/seekg 恢复流位置几个字符?它可能看起来像这样:
BufferedIStream bis(cin);
streampos pos = bis.tellg();
MyObjectType t = getObjectType(bis);
bis.seekg(pos);
作为 work-around,我目前正在将 cin 读入 EOF 到一个字符串中,并将该字符串传输到一个 istringstream,但这有许多我想避免的负面 side-affects .
我唯一能想到的另一件事是使用私有版本(仅供工厂使用)在我所有数据 类 上重载我所有 scan/read 函数,其中 header 假设已经被消费了,所以我可以完全消除对 tellg/seekg 的需要。这会工作得很好,但会引入 fair-amount 的丑陋。相比之下, tellg/seekg 与我的工厂隔离,只是两行代码。我讨厌把它扔掉。
您可以创建一个过滤流缓冲区,即从 std::streambuf
派生的 class。为了支持缓冲读取,您将覆盖 underflow()
以在输入字符被消耗后填充下一个字符缓冲区。为了支持有限搜索,先前的缓冲区不会被丢弃,而是部分保留。此外,您将覆盖 seekoff()
.
像这样应该可以解决问题:
#include <iostream>
#include <streambuf>
#include <string>
#include <cstdlib>
#include <cstring>
class bufferbuf
: public std::streambuf {
enum { size = 2000, half = size / 2 };
char buffer[size];
std::streambuf* sbuf;
std::streamoff base;
public:
bufferbuf(std::streambuf* sbuf): sbuf(sbuf), base() {
auto read = sbuf->sgetn(this->buffer, size);
this->setg(this->buffer, this->buffer, this->buffer + read);
}
int underflow() {
if (this->gptr() == this->buffer + size) {
std::memmove(this->eback(), this->eback() + half, half);
base += half;
auto read = sbuf->sgetn(this->eback() + half, half);
this->setg(this->eback(), this->eback() + half, this->eback() + half + read);
}
return this->gptr() != this->egptr()
? traits_type::to_int_type(*this->gptr())
: traits_type::eof();
}
std::streampos seekoff(off_type offset,
std::ios_base::seekdir whence,
std::ios_base::openmode which) override {
if (this->gptr() - this->eback() < -offset
|| this->egptr() - this->gptr() < offset
|| whence != std::ios_base::cur
|| !(which & std::ios_base::in)) {
return pos_type(off_type(-1));
}
this->gbump(offset);
return pos_type(this->base + (this->gptr() - this->eback()));
}
std::streampos seekpos(pos_type pos, std::ios_base::openmode which) override {
if (off_type(pos) < this->base
|| this->base + (this->egptr() - this->eback()) < off_type(pos)
|| !(which & std::ios_base::in)) {
return pos_type(off_type(-1));
}
this->setg(this->eback(), this->eback() + (off_type(pos) - this->base), this->egptr());
return pos_type(base + (this->gptr() - this->eback()));
}
};
int main() {
bufferbuf buf(std::cin.rdbuf());
std::istream in(&buf);
// ...
std::string s0, s1;
bool relative(false);
if (relative) {
while (in >> s0
&& (in.seekg(-int(s0.size()), std::ios_base::cur), in >> s1)) {
std::cout << "read "
<< "s0='" << s0 << "' " << "s1='" << s1 << "'\n";
}
}
else {
for (std::streampos pos = in.tellg();
in >> s0 && (in.seekg(pos), in >> s1); pos = in.tellg()) {
std::cout << "read "
<< "s0='" << s0 << "' " << "s1='" << s1 << "'\n";
}
}
}
上面的代码适用于几个简单的测试用例。它演示了相对定位和绝对定位的使用。一般来说,我发现在流中寻找是没有用的,因为通常每个有趣的词法分析都可以只用一个字符前瞻来完成。结果,我可能错过了位置方面的一些东西。但是,我希望上面的代码能够正常工作。