制作自定义 istream 操纵器
Making a custom istream manipulator
我想制作一个自定义 istream
操纵器,它从输入中读取 2 个字符,然后从输入中跳过 2 个字符,一直这样做直到用完所有输入。
例如,如果我有这样的代码:
std::string str;
std::cin >> skipchar >> str;
其中skipchar
是我的操纵符,如果用户输入1122334455
,str
应该包含113355
.
这是我目前得到的结果,我不知道我应该在 while 循环条件中放入什么才能使这段代码正常工作:
istream& skipchar(istream& stream)
{
char c;
while(1)
{
for (int i = 0; i < 2; ++i)
stream >> c;
for (int i = 0; i < 2; ++i)
stream.ignore(1, '[=11=]');
}
return stream;
}
如有任何帮助,我们将不胜感激。
这是个很好的问题。我不知道这是否可能。但是我实现了一些不同的东西,通过用一个名为 Skip2
的新 class 重载 >> operator
,为您提供了您想要的相同的简短语法。这是代码(我真的很喜欢写它!:-))
#include <iostream>
#include <string>
#include <istream>
#include <sstream>
using namespace std;
class Skip2 {
public:
string s;
};
istream &operator>>(istream &s, Skip2 &sk)
{
string str;
s >> str;
// build new string
ostringstream build;
int count = 0;
for (char ch : str) {
// a count "trick" to make skip every other 2 chars concise
if (count < 2) build << ch;
count = (count + 1) % 4;
}
// assign the built string to the var of the >> operator
sk.s = build.str();
// and of course, return this istream
return s;
}
int main()
{
istringstream s("1122334455");
Skip2 skip;
s >> skip;
cout << skip.s << endl;
return 0;
}
这很棘手; istream 操纵器不作为 "filters" 在流上运行,而是作为单次操作。标准提供的 istream 操纵器(noskipws
、hex
等)通过设置和清除流上的标志来完成它们的工作,因此它们只公开已经可用的功能。
但是,可以创建一个过滤 streambuf 包装 cin
(或任何输入流)的 streambuf,并使用操纵器安装或删除它:
struct skipbuf : std::streambuf {
std::unique_ptr<std::streambuf> src;
int i;
char buf[4];
skipbuf(std::streambuf* src) : std::streambuf{*src}, src{src} {
setg(buf, buf + 2, buf + 2);
}
std::streambuf* unwrap() {
while (buf + i != gptr())
src->sputbackc(buf[--i]);
return src.release();
}
std::streambuf::int_type underflow() override {
setg(buf, buf, buf + std::min(i = src->sgetn(buf, 4), 2));
return i ? buf[0] : traits_type::eof();
}
};
std::istream& skipchar(std::istream& is) {
is.rdbuf(new skipbuf{is.rdbuf()});
return is;
}
std::istream& noskipchar(std::istream& is) {
if (auto* buf = dynamic_cast<skipbuf*>(is.rdbuf()))
delete (is.rdbuf(buf->unwrap()), buf);
return is;
}
用法示例:
int main() {
std::istringstream iss{"1122334455 hello"};
std::string s1, s2;
iss >> skipchar >> s1 >> noskipchar >> s2;
std::cout << s1 << ' ' << s2 << std::endl;
}
预期输出(run it online):
113355 hello
我想制作一个自定义 istream
操纵器,它从输入中读取 2 个字符,然后从输入中跳过 2 个字符,一直这样做直到用完所有输入。
例如,如果我有这样的代码:
std::string str;
std::cin >> skipchar >> str;
其中skipchar
是我的操纵符,如果用户输入1122334455
,str
应该包含113355
.
这是我目前得到的结果,我不知道我应该在 while 循环条件中放入什么才能使这段代码正常工作:
istream& skipchar(istream& stream)
{
char c;
while(1)
{
for (int i = 0; i < 2; ++i)
stream >> c;
for (int i = 0; i < 2; ++i)
stream.ignore(1, '[=11=]');
}
return stream;
}
如有任何帮助,我们将不胜感激。
这是个很好的问题。我不知道这是否可能。但是我实现了一些不同的东西,通过用一个名为 Skip2
的新 class 重载 >> operator
,为您提供了您想要的相同的简短语法。这是代码(我真的很喜欢写它!:-))
#include <iostream>
#include <string>
#include <istream>
#include <sstream>
using namespace std;
class Skip2 {
public:
string s;
};
istream &operator>>(istream &s, Skip2 &sk)
{
string str;
s >> str;
// build new string
ostringstream build;
int count = 0;
for (char ch : str) {
// a count "trick" to make skip every other 2 chars concise
if (count < 2) build << ch;
count = (count + 1) % 4;
}
// assign the built string to the var of the >> operator
sk.s = build.str();
// and of course, return this istream
return s;
}
int main()
{
istringstream s("1122334455");
Skip2 skip;
s >> skip;
cout << skip.s << endl;
return 0;
}
这很棘手; istream 操纵器不作为 "filters" 在流上运行,而是作为单次操作。标准提供的 istream 操纵器(noskipws
、hex
等)通过设置和清除流上的标志来完成它们的工作,因此它们只公开已经可用的功能。
但是,可以创建一个过滤 streambuf 包装 cin
(或任何输入流)的 streambuf,并使用操纵器安装或删除它:
struct skipbuf : std::streambuf {
std::unique_ptr<std::streambuf> src;
int i;
char buf[4];
skipbuf(std::streambuf* src) : std::streambuf{*src}, src{src} {
setg(buf, buf + 2, buf + 2);
}
std::streambuf* unwrap() {
while (buf + i != gptr())
src->sputbackc(buf[--i]);
return src.release();
}
std::streambuf::int_type underflow() override {
setg(buf, buf, buf + std::min(i = src->sgetn(buf, 4), 2));
return i ? buf[0] : traits_type::eof();
}
};
std::istream& skipchar(std::istream& is) {
is.rdbuf(new skipbuf{is.rdbuf()});
return is;
}
std::istream& noskipchar(std::istream& is) {
if (auto* buf = dynamic_cast<skipbuf*>(is.rdbuf()))
delete (is.rdbuf(buf->unwrap()), buf);
return is;
}
用法示例:
int main() {
std::istringstream iss{"1122334455 hello"};
std::string s1, s2;
iss >> skipchar >> s1 >> noskipchar >> s2;
std::cout << s1 << ' ' << s2 << std::endl;
}
预期输出(run it online):
113355 hello