I need to make a custom istream manipulator that reads 5 characters from input, then skip 5 characters from input, and does it to the end of the string. Example:
string line;
cin >> skipchar >> line;
This is that I did, but it doesn't work for some reason. Also, it would be better, if I don't use <sstream>
struct membuf : streambuf
{
membuf(char* begin, char* end) {
this->setg(begin, begin, end);
}
};
istream& skipchar(istream& stream)
{
string temp;
getline(stream, temp);
char *str = new char[temp.size() 1];
strcpy(str, temp.c_str());//get string and convert to char*
char* res = new char[strlen(str)];
for (int i=0,j=0;i<strlen(str);i )
if ((i / 5) % 2 == 0) //get first 5, then 5 skip, etc
res[j ] = str[i];
membuf b(res, res strlen(res)); //copy str to buffer
stream.rdbuf(&b);
return stream;
}
int main()
{
string str;
cout << "enter smth:\n";
cin >> skipchar >> str;
cout << "entered string: " << str;
return 0;
}
CodePudding user response:
You did not show your input, but I don't think getline()
would be appropriate to use in this situation. operator>>
is meant to read a single word, not a whole line.
In any case, you are leaking both char[]
arrays that you allocate. You need to delete[]
them when you are done using them. For the str
array (which FYI, you don't actually need, as you could just copy characters from the temp
string directly into res
instead), you can just delete[]
it before exiting. But for res
, the membuf
would have to hold on to that pointer and delete[]
it when the membuf
itself is no longer being used.
But, more importantly, your use of membuf
is simply wrong. You are creating it as a local variable of skipchar()
, so it will be destroyed when skipchar()
exits, leaving the stream
with a dangling pointer to an invalid object. The streambuf*
pointer you assign to the stream
must remain valid for the entire duration that it is assigned to the istream
, which means creating the membuf
object with new
, and then the caller will have to remember to manually delete
it at a later time (which kind of defeats the purpose of using operator>>
). However, a stream manipulator really should not change the rdbuf
that the stream
is pointing at in the first place, since there is not a good way to restore the previous streambuf
after subsequent read operations are finished (unless you define another manipulator to handle that, ie cin >> skipchar >> str >> stopskipchar;
).
In this situation, I would suggest a different approach. Don't make a stream manipulator that assigns a new streambuf
to the stream
, thus affecting all subsequent operator>>
calls. Instead, make a manipulator that takes a reference to the output variable, and then reads from the stream
and outputs only what is needed (similar to how standard manipulators like std::quoted
and std::get_time
work), eg:
struct skipchars
{
string &str;
};
istream& operator>>(istream& stream, skipchars output)
{
string temp;
if (stream >> temp) {
for (size_t i = 0; i < temp.size(); i = 10) {
output.str = temp.substr(i, 5);
}
}
return stream;
}
int main()
{
string str;
cout << "enter smth:\n";
cin >> skipchars{str};
cout << "entered string: " << str;
return 0;
}
Alternatively:
struct skipcharsHelper
{
string &str;
};
istream& operator>>(istream& stream, skipcharsHelper output)
{
string temp;
if (stream >> temp) {
for (size_t i = 0; i < temp.size(); i = 10) {
output.str = temp.substr(i, 5);
}
}
return stream;
}
skipcharsHelper skipchars(string &str)
{
return skipcharsHelper{str};
}
int main()
{
string str;
cout << "enter smth:\n";
cin >> skipchars(str);
cout << "entered string: " << str;
return 0;
}