How to achieve scanf("%d # %d",&a,&b);
sort of effect with cin
in C++ ?

- 3,132
- 3
- 29
- 32
-
2You may find [`std::istream::ignore`](http://en.cppreference.com/w/cpp/io/basic_istream/ignore) useful. – WhozCraig Feb 13 '14 at 20:35
-
There are a couple options, the simplest of which is to extract the input into a string and check for "integer-ness". If it's an integer, convert it to an integer. – David G Feb 13 '14 at 20:35
-
@WhozCraig I think that makes it more compilicated. How is one supposed to know when to stop ignoring characters? I suppose you can check `peek()` for the next incoming character, and see if it's integral (and if not `ignore()` it) but why go through all that trouble? – David G Feb 13 '14 at 20:41
-
@0x499602D2 Because you have to do it anyway, even if you `std::getline` or `std::cin >> str;` and try to convert the result. The OP's format string is ignoring a specific character, `'#'`. Doing this by blanket try-to-convert-and-fail seems more overkill to me than knowing what you're ignoring (which the OP does) and simply skipping it. It wouldn't be equivalent to the original format string instruction: extract int, skip whitespace `#` whitespace, extract int. – WhozCraig Feb 13 '14 at 20:46
-
@WhozCraig I always assume the OP *doesn't* know what's in between the important data. I assumed you were suggesting a solution that would work for any arbitrary file format. – David G Feb 13 '14 at 20:52
-
@0x499602D2 I wasn't. It was specifically suggested for the specific format string the OP posted. – WhozCraig Feb 13 '14 at 20:57
-
@0x499602D2 Read the post. OP _explicitly_ states "skipping expected characters like scanf()" I agree with WhozCraig. istream::ignore is the easiest drop in given the OPs question. – Lother Feb 13 '14 at 21:23
5 Answers
You can skip the #
by extracting it into a character:
std::istringstream iss("10 # 20");
int main()
{
int a, b; char hash;
iss >> a >> hash >> b;
assert(a == 10 && b == 20);
}

- 94,763
- 41
- 167
- 253
You could create your own stream manipulator. It is fairly easy.
#include <ios>
#include <iostream>
using namespace std;
// skips the number of characters equal to the length of given text
// does not check whether the skipped characters are the same as it
struct skip
{
const char * text;
skip(const char * text) : text(text) {}
};
std::istream & operator >> (std::istream & stream, const skip & x)
{
ios_base::fmtflags f = stream.flags();
stream >> noskipws;
char c;
const char * text = x.text;
while (stream && *text++)
stream >> c;
stream.flags(f);
return stream;
}
int main()
{
int a, b;
cin >> a >> skip(" # ") >> b;
cout << a << ", " << b << endl;
return 0;
}

- 13,814
- 3
- 48
- 61
There isn't a direct function inside the istream
class that mimics it, unfortunately. There are functions that you might be able to use to manipulate the stream and get the correct input, but I'm not familiar with how they work so I couldn't tell you how.
My best suggestion on how I would personally do it is to use getline()
to put the input into a string and then from there I would do a few checks to see if it matches the format. So in your case I would grab the first substring up until the first space, make sure it's a valid decimal, check to make sure the pound sign ('#') is in the correct spot, then grab the ending number to make sure it's valid. If any one of those three objects are incorrect I would set some boolean variable to false
to kick out or return or something to indicate that the input was invalid and not the correct format.
Pseudo Code:
...
getline(cin,myStr);
while(!formatCheck(myStr))
{
cout<<"Not valid format for input";
getline(cin,myStr);
}
...
bool formatCheck(string str)
{
string firstPart=str.subString(0,firstSpaceLocation);
string middle=str[firstSpaceLocation+1];
string lastPart=str.subString(firstSpaceLocation+3,end);
if(first part not a valid number || middle!="#" || last part not a valid number)
{
return false;
}
return true;
}

- 490
- 2
- 16
-
-
That's possible. All you would need to do is overload the `>>` operator for your class and include that stuff in the method for it. Then all you'd need to do is just `cin` into your class and it will handle it for you. Just look into operator overloading and it should be pretty straight forward. – Josh Braun Feb 15 '14 at 03:39
Here's another way. You can classify #
as a whitespace character through the std::ctype<char>
facet imbued in the locale:
#include <iostream>
#include <sstream>
#include <vector>
namespace detail
{
enum options { add, remove };
class ctype : public std::ctype<char>
{
private:
static mask* get_table(const std::string& ws, options opt)
{
static std::vector<mask> table(classic_table(),
classic_table() + table_size);
for (char c : ws)
{
if (opt == add)
table[c] |= space;
else if (opt == remove)
table[c] &= ~space;
}
return &table[0];
}
public:
ctype(const std::string& ws, options opt)
: std::ctype<char>(get_table(ws, opt)) { }
};
}
class adjustws_impl
{
public:
adjustws_impl(const std::string& ws, detail::options opt) :
m_ws(ws),
m_opt(opt)
{ }
friend std::istream& operator>>(std::istream& is,
const adjustws_impl& manip)
{
is.imbue(std::locale(is.getloc(),
new detail::ctype(manip.m_ws, manip.m_opt)));
return is;
}
private:
std::string m_ws;
detail::options m_opt;
};
adjustws_impl setws(const std::string& ws)
{
return adjustws_impl(ws, detail::add);
}
adjustws_impl unsetws(const std::string& ws)
{
return adjustws_impl(ws, detail::remove);
}
int main()
{
std::istringstream iss("10 # 20");
int a, b;
iss >> setws("#");
iss >> a >> b;
iss >> unsetws("#");
std::cout << a << ' ' << b; // 10 20
}

- 94,763
- 41
- 167
- 253
You can skip the #
, or any single character, by using std::istream::ignore
std::istringstream sstr("1024 # 768");
int main()
{
int a, b;
sstr >> a;
sstr.ignore(256,'#'); // ignore until hash character
sstr >> b;
std::cout << "a: " << a << " b: " << b << std::endl;
}

- 801
- 1
- 10
- 14