Alternative to sscanf_s in C++ - c++

result = sscanf_s(line.c_str(), "data (%d,%d)", &a, &b);
In the code above I am using sscanf_s to extract two integer values from the given string line. Is there another way, more object-oriented, of doing that in C++11? (std::stringstream and/or regular expressions?)
EDIT: I tried two solutions, first one doesn't work, second one does
// solution one (doesn't work)
// let line = "data (3,4)"
std::regex re("data (.*,.*)");
std::smatch m;
if (std::regex_search(line, m, re) )
cout << m[0] << " "<< m[1]; // I get the string "data (3,4) (3,4)"
// solution two (works but is somewhat ugly)
std::string name;
char openParenthesis;
char comma;
char closeParenthesis;
int x = 0, y = 0;
std::istringstream stream(line);
stream >> name >> openParenthesis >> a >> comma >> b >> closeParenthesis;
if( name=="data" && openParenthesis == '(' && comma == ',' && closeParenthesis == ')' )
{
a = x;
b = y;
}
EDIT 2: With Shawn's input, the following works perfectly:
std::regex re(R"(data \(\s*(\d+),\s*(\d+)\))");
std::smatch m;
if (std::regex_search(line, m, re) )
{
a = std::stoi(m[1]);
b = std::stoi(m[2]);
}

If it has not to be regex per se, you could use Boost.Spirit. The following is a slight modification of this example and gives you any number of comma-separated integers in a vector. (That is not exactly what you requested, but showing off a bit of what else would be possible, and also I didn't want to put more effort into changing the example).
This works on iterators, i.e. strings as well as streams. It's also trivially expandable to more complex grammars, and you can create stand-alone grammar objects you can re-use, or combine into yet more complex grammars. (Not demonstrated here.)
#include "boost/spirit/include/qi.hpp"
#include "boost/spirit/include/phoenix_core.hpp"
#include "boost/spirit/include/phoenix_operator.hpp"
#include "boost/spirit/include/phoenix_stl.hpp"
#include <iostream>
#include <string>
#include <vector>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
template < typename Iterator >
bool parse_data( Iterator first, Iterator last, std::vector< int > & v )
{
bool r = qi::phrase_parse( first, last,
// Begin grammar
(
qi::lit( "data" ) >> '('
>> qi::int_[ phoenix::push_back( phoenix::ref( v ), qi::_1 ) ]
>> *( ',' >> qi::int_[ phoenix::push_back( phoenix::ref( v ), qi::_1 ) ] )
>> ')'
),
// End grammar
ascii::space );
if ( first != last ) // fail if we did not get a full match
{
return false;
}
return r;
}
int main()
{
std::string input = "data (38,4)";
std::vector< int > v;
if ( parse_data( input.begin(), input.end(), v ) )
{
std::cout << "Read:\n";
for ( auto i : v )
{
std::cout << i << "\n";
}
}
else
{
std::cout << "Failed.\n";
}
return 0;
}

Related

How can I parse a bracketed string to a list of string with a given delimitor

My task is to parse a bracketed string, like
[foo | bar | foobar], to a vector of std::strings.
In this case,
the vector should end up with the contents {"foo" , "bar", "foobar"}.
These brackets can be nested. For example, the given bracketed string
[[john | doe] | [ bob | dylan]]
would become { "[john | doe]" , "[bob | dylan] }"
The best I could manage so far is
int main(int argc, char ** argv)
{
const std::string input {argv[1]};
std::vector<std::string> res;
qi::phrase_parse(input.cbegin(), input.cend(),
'['
>> *qi::lexeme[ +(qi::char_ - '|') >> '|']
> -qi::lexeme[ +(qi::char_ - ']') >> ']' ],
qi::space ,
res);
for (const auto& v: res)
std::cout << v <<std::endl;
return 0;
}
which fails miserably for the nested case. Can somebody please point me in the right direction?
Note #1: Nested cases can be more than one.
Note #2: I welcome any simpler solutions, even without using Boost Spirit.
Here's a simple C++ parser based on the assumption that brackets are balanced, i.e. every [ has a ] there.
bracket is the number of opening brackets. We make crucial decisions when that number is 1.
#include <iostream>
#include <vector>
#include <string>
#include <string_view>
bool edge(const int num){
return num == 1;
}
int main(){
std::vector<std::string> all;
std::string line;
// std::getline(std::cin, line);
line = "[[john | doe] | [ bob | dylan]]";
int bracket = 0;
std::string::size_type start = 0;
for(int i = 0; i < line.size(); i++){
const char c = line[i];
if(c == '['){
bracket++;
if(edge(bracket)){
start = i + 1;
}
}
if(c == ']'){
if(edge(bracket)){
all.push_back(line.substr(start, i - start));
}
bracket--;
}
if(c == '|' && edge(bracket)){
all.push_back(line.substr(start, i - start));
start = i + 1;
}
}
for(std::string_view t : all){
std::cout << t << std::endl;
}
}
If you want nested lists of strings, first you'll need a result that can store nested lists. Luckily, in C++17 you can have vectors of forward references (as long as their defined at some point). So you can make a type that is a list, where every item is either a string or another list:
struct Expr : std::vector<
boost::variant<
std::string,
Expr>>
{
using std::vector<boost::variant<std::string, Expr>>::vector;
};
After the grammar is pretty simple. Note that it's recursive - Term can have an Expr nested into it:
WORD = /[^\[\|\]]+/
Term = WORD | Expr
Expr = '[' Term ('|' Term)* ']';
You can express each rule separately. Boost Spirit Qi conveniently has the % operator, which parses a delimited list and inserts it into a container.
using It = std::string::const_iterator;
using Sk = qi::space_type;
qi::rule<It, std::string(), Sk> word;
qi::rule<It, boost::variant<std::string, Expr>(), Sk> term;
qi::rule<It, Expr(), Sk> expr;
word = +(qi::char_ - '[' - '|' - ']');
term = word | expr;
expr = '[' >> (term % '|') >> ']';
Then qi::phrase_parse will do what you want:
Expr res;
qi::phrase_parse(input.cbegin(), input.cend(), expr, qi::space, res);
Demo: https://godbolt.org/z/5W993s
This simpler version seems to be what you want:
qi::phrase_parse(input.cbegin(), input.cend(),
'['
>> qi::lexeme[ +~qi::char_("|]") ] % '|'
>> ']',
qi::space,
res);
It will parse:
"foo "
"bar "
"foobar"
Maybe you didn't actually want the spaces as part of the matches. Then it can be even simpler:
qi::phrase_parse(input.cbegin(), input.cend(),
'['
>> qi::lexeme[ +(qi::graph - qi::char_("|]")) ] % '|'
>> ']',
qi::space,
res);
See it Live On Coliru
Note: if you have C++14 consider using X3: Live On Coliru. That will be a lot faster to compile

How to extract the string pattern in C++ efficiently?

I have a pattern that in the following format:
AUTHOR, "TITLE" (PAGES pp.) [CODE STATUS]
For example, I have a string
P.G. Wodehouse, "Heavy Weather" (336 pp.) [PH.409 AVAILABLE FOR LENDING]
I want to extract
AUTHOR = P.G. Wodehouse
TITLE = Heavy Weather
PAGES = 336
CODE = PH.409
STATUS = AVAILABLE FOR LENDING
I only know how to do that in Python, however, are there any efficient way to do the same thing in C++?
Exactly the same way as in Python. C++11 has regular expressions (and for earlier C++, there's Boost regex.) As for the read loop:
std::string line;
while ( std::getline( file, line ) ) {
// ...
}
is almost exactly the same as:
for line in file:
# ...
The only differences are:
The C++ version will not put the trailing '\n' in the buffer. (In general, the C++ version may be less flexible with regards to end of line handling.)
In case of a read error, the C++ version will break the loop; the Python version will raise an exception.
Neither should be an issue in your case.
EDIT:
It just occurs to me that while regular expressions in C++ and in Python are very similar, the syntax for using them isn't quite the same. So:
In C++, you'd normally declare an instance of the regular expression before using it; something like Python's re.match( r'...', line ) is theoretically possible, but not very idiomatic (and it would still involve explicitly constructuing a regular expression object in the expression). Also, the match function simply returns a boolean; if you want the captures, you need to define a separate object for them. Typical use would probably be something like:
static std::regex const matcher( "the regular expression" );
std::smatch forCaptures;
if ( std::regex_match( line, forCaptures, matcher ) ) {
std::string firstCapture = forCaptures[1];
// ...
}
This corresponds to the Python:
m = re.match( 'the regular expression', line )
if m:
firstCapture = m.group(1)
# ...
EDIT:
Another answer has suggested overloading operator>>; I heartily concur. Just out of curiousity, I gave it a go; something like the following works well:
struct Book
{
std::string author;
std::string title;
int pages;
std::string code;
std::string status;
};
std::istream&
operator>>( std::istream& source, Book& dest )
{
std::string line;
std::getline( source, line );
if ( source )
{
static std::regex const matcher(
R"^(([^,]*),\s*"([^"]*)"\s*\((\d+) pp.\)\s*\[(\S+)\s*([^\]]*)\])^"
);
std::smatch capture;
if ( ! std::regex_match( line, capture, matcher ) ) {
source.setstate( std::ios_base::failbit );
} else {
dest.author = capture[1];
dest.title = capture[2];
dest.pages = std::stoi( capture[3] );
dest.code = capture[4];
dest.status = capture[5];
}
}
return source;
}
Once you've done this, you can write things like:
std::vector<Book> v( (std::istream_iterator<Book>( inputFile )),
(std::istream_iterator<Book>()) );
And load an entire file in the initialization of a vector.
Note the error handling in the operator>>. If a line is misformed, we set failbit; this is the standard convention in C++.
EDIT:
Since there's been so much discussion: the above is fine for small, one time programs, things like school projects, or one time programs which will read the current file, output it in a new format, and then be thrown away. In production code, I would insist on support for comments and empty lines; continuing in case of error, in order to report multiple errors (with line numbers), and probably continuation lines (since titles can get long enough to become unwieldly). It's not practical to do this with operator>>, if for no other reason than the need to output line numbers, so I'd use a parser along the following line:
int
getContinuationLines( std::istream& source, std::string& line )
{
int results = 0;
while ( source.peek() == '&' ) {
std::string more;
std::getline( source, more ); // Cannot fail, because of peek
more[0] = ' ';
line += more;
++ results;
}
return results;
}
void
trimComment( std::string& line )
{
char quoted = '\0';
std::string::iterator position = line.begin();
while ( position != line.end() && (quoted != '\0' || *position == '#') ) {
if ( *position == '\' && std::next( position ) != line.end() ) {
++ position;
} else if ( *position == quoted ) {
quoted = '\0';
} else if ( *position == '\"' || *position == '\'' ) {
quoted = *position;
}
++ position;
}
line.erase( position, line.end() );
}
bool
isEmpty( std::string const& line )
{
return std::all_of(
line.begin(),
line.end(),
[]( unsigned char ch ) { return isspace( ch ); } );
}
std::vector<Book>
parseFile( std::istream& source )
{
std::vector<Book> results;
int lineNumber = 0;
std::string line;
bool errorSeen = false;
while ( std::getline( source, line ) ) {
++ lineNumber;
int extraLines = getContinuationLines( source, line );
trimComment( line );
if ( ! isEmpty( line ) ) {
static std::regex const matcher(
R"^(([^,]*),\s*"([^"]*)"\s*\((\d+) pp.\)\s*\[(\S+)\s*([^\]]*)\])^"
);
std::smatch capture;
if ( ! std::regex_match( line, capture, matcher ) ) {
std::cerr << "Format error, line " << lineNumber << std::endl;
errorSeen = true;
} else {
results.emplace_back(
capture[1],
capture[2],
std::stoi( capture[3] ),
capture[4],
capture[5] );
}
}
lineNumber += extraLines;
}
if ( errorSeen ) {
results.clear(); // Or more likely, throw some sort of exception.
}
return results;
}
The real issue here is how you report the error to the caller; I suspect that in most cases, and exception would be appropriate, but depending on the use case, other alternatives may be valid as well. In this example, I just return an empty vector. (The interaction between comments and continuation lines probably needs to be better defined as well, with modifications according to how it has been defined.)
Your input string is well delimited so I'd recommend using an extraction operator over a regex, for speed and for ease of use.
You'd first need to create a struct for your books:
struct book{
string author;
string title;
int pages;
string code;
string status;
};
Then you'd need to write the actual extraction operator:
istream& operator>>(istream& lhs, book& rhs){
lhs >> ws;
getline(lhs, rhs.author, ',');
lhs.ignore(numeric_limits<streamsize>::max(), '"');
getline(lhs, rhs.title, '"');
lhs.ignore(numeric_limits<streamsize>::max(), '(');
lhs >> rhs.pages;
lhs.ignore(numeric_limits<streamsize>::max(), '[');
lhs >> rhs.code >> ws;
getline(lhs, rhs.status, ']');
return lhs;
}
This gives you a tremendous amount of power. For example you can extract all the books from an istream into a vector like this:
istringstream foo("P.G. Wodehouse, \"Heavy Weather\" (336 pp.) [PH.409 AVAILABLE FOR LENDING]\nJohn Bunyan, \"The Pilgrim's Progress\" (336 pp.) [E.1173 CHECKED OUT]");
vector<book> bar{ istream_iterator<book>(foo), istream_iterator<book>() };
Use flex (it generates C or C++ code, to be used as a part or as the full program)
%%
^[^,]+/, {printf("Autor: %s\n",yytext );}
\"[^"]+\" {printf("Title: %s\n",yytext );}
\([^ ]+/[ ]pp\. {printf("Pages: %s\n",yytext+1);}
..................
.|\n {}
%%
(untested)
Here's the code:
#include <iostream>
#include <cstring>
using namespace std;
string extract (string a)
{
string str = "AUTHOR = "; //the result string
int i = 0;
while (a[i] != ',')
str += a[i++];
while (a[i++] != '\"');
str += "\nTITLE = ";
while (a[i] != '\"')
str += a[i++];
while (a[i++] != '(');
str += "\nPAGES = ";
while (a[i] != ' ')
str += a[i++];
while (a[i++] != '[');
str += "\nCODE = ";
while (a[i] != ' ')
str += a[i++];
while (a[i++] == ' ');
str += "\nSTATUS = ";
while (a[i] != ']')
str += a[i++];
return str;
}
int main ()
{
string a;
getline (cin, a);
cout << extract (a) << endl;
return 0;
}
Happy coding :)

Parse a C-string of floating numbers

I have a C-string which contains a list of floating numbers separated by commas and spaces. Each pair of numbers is separated by one (or more) spaces and represents a point where the x and y fields are separated by a comma (and optionally by spaces).
" 10,9 2.5, 3 4 ,150.32 "
I need to parse this string in order to fill a list of Point(x, y).
Following is my current implementation:
const char* strPoints = getString();
std::istringstream sstream(strPoints);
float x, y;
char comma;
while (sstream >> x >> comma >> y)
{
myList.push(Point(x, y));
}
Since I need to parse a lot (up to 500,000) of these strings I'm wondering if there is a faster solution.
Look at Boost Spirit:
How to parse space-separated floats in C++ quickly?
It supports NaN, positive and negative infinity just fine. Also it allows you to express the constraining grammar succinctly.
Simple adaptation of the code
Here is the adapted sample for your grammar:
struct Point { float x,y; };
typedef std::vector<Point> data_t;
// And later:
bool ok = phrase_parse(f,l,*(double_ > ',' > double_), space, data);
The iterators can be any iterators. So you can hook it up with your C string just fine.
Here's a straight adaptation of the linked benchmark case. This shows you how to parse from any std::istream or directly from a memory mapped file.
Live On Coliru
Further optimizations (strictly for C strings)
Here's a version that doesn't need to know the length of the string up front (this is neat because it avoids the strlen call in case you didn't have the length available):
template <typename OI>
static inline void parse_points(OI out, char const* it, char const* last = std::numeric_limits<char const*>::max()) {
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
bool ok = qi::phrase_parse(it, last,
*(qi::double_ >> ',' >> qi::double_) [ *phx::ref(out) = phx::construct<Point>(qi::_1, qi::_2) ],
qi::space);
if (!ok || !(it == last || *it == '\0')) {
throw it; // TODO proper error reporting?
}
}
Note how I made it take an output iterator so that you get to decide how to accumulate the results. The obvious wrapper to /just/ parse to a vector would be:
static inline data_t parse_points(char const* szInput) {
data_t pts;
parse_points(back_inserter(pts), szInput);
return pts;
}
But you can also do different things (like append to an existing container, that could have reserved a known capacity up front etc.). Things like this often allow truly optimized integration in the end.
Here's that code fully demo-ed in ~30 lines of essential code:
Live On Coliru
Extra Awesome Bonus
To show off the flexibility of this parser; if you just wanted to check the input and get a count of the points, you can replace the output iterator with a simple lambda function that increments a counter instead of adds a newly constructed point.
int main() {
int count = 0;
parse_points( " 10,9 2.5, 3 4 ,150.32 ", boost::make_function_output_iterator([&](Point const&){count++;}));
std::cout << "elements in sample: " << count << "\n";
}
Live On Coliru
Since everything is inlined the compiler will notice that the whole Point doesn't need to be constructed here and eliminate that code: http://paste.ubuntu.com/9781055/
The main function is seen directly invoking the very parser primitives. Handcoding the parser won't get you better tuning here, at least not without a lot of effort.
I got much better performance parsing out the points using a combination of std::find and std::strtof and the code wasn't much more complicated. Here's the test I ran:
#include <iostream>
#include <sstream>
#include <random>
#include <chrono>
#include <cctype>
#include <algorithm>
#include <cstdlib>
#include <forward_list>
struct Point { float x; float y; };
using PointList = std::forward_list<Point>;
using Clock = std::chrono::steady_clock;
using std::chrono::milliseconds;
std::string generate_points(int n) {
static auto random_generator = std::mt19937{std::random_device{}()};
std::ostringstream oss;
std::uniform_real_distribution<float> distribution(-1, 1);
for (int i=0; i<n; ++i) {
oss << distribution(random_generator) << " ," << distribution(random_generator) << "\t \n";
}
return oss.str();
}
PointList parse_points1(const char* s) {
std::istringstream iss(s);
PointList points;
float x, y;
char comma;
while (iss >> x >> comma >> y)
points.push_front(Point{x, y});
return points;
}
inline
std::tuple<Point, const char*> parse_point2(const char* x_first, const char* last) {
auto is_whitespace = [](char c) { return std::isspace(c); };
auto x_last = std::find(x_first, last, ',');
auto y_first = std::find_if_not(std::next(x_last), last, is_whitespace);
auto y_last = std::find_if(y_first, last, is_whitespace);
auto x = std::strtof(x_first, (char**)&x_last);
auto y = std::strtof(y_first, (char**)&y_last);
auto next_x_first = std::find_if_not(y_last, last, is_whitespace);
return std::make_tuple(Point{x, y}, next_x_first);
}
PointList parse_points2(const char* i, const char* last) {
PointList points;
Point point;
while (i != last) {
std::tie(point, i) = parse_point2(i, last);
points.push_front(point);
}
return points;
}
int main() {
auto s = generate_points(500000);
auto time0 = Clock::now();
auto points1 = parse_points1(s.c_str());
auto time1 = Clock::now();
auto points2 = parse_points2(s.data(), s.data() + s.size());
auto time2 = Clock::now();
std::cout << "using stringstream: "
<< std::chrono::duration_cast<milliseconds>(time1 - time0).count() << '\n';
std::cout << "using strtof: "
<< std::chrono::duration_cast<milliseconds>(time2 - time1).count() << '\n';
return 0;
}
outputs:
using stringstream: 1262
using strtof: 120
You can first try to disable the sychronization with the C I/O:
std::ios::sync_with_stdio(false);
Source: Using scanf() in C++ programs is faster than using cin?
You can also try to use alternatives to iostream:
boost_lexical_cast and define BOOST_LEXICAL_CAST_ASSUME_C_LOCALE
scanf
I think you should give the sync_with_stdio(false) a try. The other alternatives require more coding, and I'm not sure that you will win much (if any).

Is there a way to ignore a char when getting user input for array?

in C++ I am creating a program that asks a user for a date in the following format: MM/DD/YYYY. Since the date is an int and must be an int, I figured the most logical way to get this in one line is if I was to use an array.
So I created something like this...
int dateArray[3];
for (int i=0; i<3; i++)
cin >> dateArray[i];
int month = dateArray[0];
...etc
My question is if a user enters "1/23/1980" is there a way I can ignore the / that the user inputs?
Thank you.
You can ignore one character using std::istream::ignore(). Since you probably only want to ignore intervening characters, you'd need to know when to ignore and when not to ignore. For a date I would personally not bother but just read the three terms:
if (((std::cin >> month).ignore() >> year).ignore() >> day) {
// do something with the date
}
else {
// deal with input errors
}
I would actually also be inclined to check that the correct separator is received and probably just create a manipulator for this purpose:
std::istream& slash(std::istream& in) {
if ((in >> std::ws).peek() != '/') {
in.setstate(std::ios_base::failbit);
}
else {
in.ignore();
}
return in;
}
// ....
if (std::cin >> month >> slash >> year >> slash >> day) {
// ...
}
... and, obviously, I would check in all cases that the input is correct.
Consider using C++11 regular expression library support for this type of parsing. For instance
#include <iostream>
#include <iterator>
#include <regex>
#include <string>
int main()
{
std::string string{ "12/34/5678" };
std::regex regex{ R"((\d{2})/(\d{2})/(\d{4}))" };
auto regexIterator = std::sregex_iterator( std::begin( string ), std::end( string ), regex );
std::vector< std::string > mdy;
for( auto matchItor = regexIterator; matchItor != std::sregex_iterator{}; ++matchItor )
{
std::smatch match{ *matchItor };
mdy.push_back( match.str() );
}
const std::size_t mdySize{ mdy.size() };
for( std::size_t matchIndex{ 0 }; matchIndex < mdySize; ++matchIndex )
{
if( matchIndex != mdySize && matchIndex != 0 ) std::cout << '/';
std::cout << mdy.at( matchIndex );
}
}
I would not ignore it; it's part of your format, even though you do not need to keep it around indefinitely.
I would read it into a char and make sure that it is, in fact, a /.

Extracting integer values from string elements and adding up

I have the following lines of code:
vector<string> c;
string a;
for(int i=0;i<4;i++){
cin>>a;
c.push_back(a);
}
If I provide input as:
120$,132$,435$,534$
How can I extract the integer values separately and add them up to get the total value?
You can use e.g. std::getline with a custome "line" separator using the comma, strip the last character from the string (the '$') and use std::stoi to convert to an integer:
std::vector<int> c;
for (int i = 0; i < 4; i++)
{
std::string a;
std::getline(std::cin, a, ',');
a = a.substr(a.length() - 1); // Remove trailing dollar sign
c.push_back(std::stoi(a));
}
Edit: Using std::accumulate:
int sum = std::accumulate(c.begin(), c.end(), 0);
Edit 2: Using std::strtol instead of std::stoi:
The function std::stoi is new in the latest C++ standard (C++11) and it not supported in all standard libraries yet. Then you can use the older C function strtol:
c.push_back(int(std::strtol(a.c_str(), 0, 10)));
You can use regex and streams:
#include <regex>
#include <iostream>
#include <sstream>
const std::string Input("120$,132$,435$,534$");
int main(int argc, char **argv)
{
const std::regex r("[0-9]+");
int Result = 0;
for (std::sregex_iterator N(Input.begin(), Input.end(), r); N != std::sregex_iterator(); ++N)
{
std::stringstream SS(*N->begin());
int Current = 0;
SS >> Current;
Result += Current;
std::cout << Current << '\n';
}
std::cout << "Sum = " << Result;
return 0;
}
Output:
120
132
435
534
Sum = 1221
If you must ensure that the number is followed by a '$' then change the regex to: "[0-9]+\\$" the stringstream part will ignore the trailing '$' in the number conversion:
#include <regex>
#include <iostream>
#include <sstream>
const std::string Input("120$,132$,435$,534$,1,2,3");
int main(int argc, char **argv)
{
const std::regex r("[0-9]+\\$");
int Result = 0;
for (std::sregex_iterator N(Input.begin(), Input.end(), r); N != std::sregex_iterator(); ++N)
{
std::stringstream SS(*N->begin());
int Current = 0;
SS >> Current;
Result += Current;
std::cout << Current << '\n';
}
std::cout << "Sum = " << Result;
return 0;
}
Output:
120
132
435
534
Sum = 1221
If the input isn't too large (and particularly if it comes as a single
line), the simplest solution is to pack it all into a string, and parse
that, creating a std::istringstream to convert each of the numeric
fields (or using boost::lexical_cast<>, if by some odd chance it has
the appropriate semantics—it normally does when translating a
string to a built-in numeric type). For something this simple, it's
possible, however, to read directly from a stream, however:
std::istream&
ignoreDollar( std::istream& stream )
{
if ( stream.peek() == '$' ) {
stream.get();
}
return stream;
}
std::istream&
checkSeparator( std::istream& stream )
{
if ( stream.peek() == ',' ) {
stream.get();
} else {
stream.setstate( std::ios_base::failbit );
}
return stream;
}
std::vector<int> values;
int value;
while ( std::cin >> value ) {
values.push_back( value );
std::cin >> ignoreDollar >> checkSeparator;
}
int sum = std::accumulate( values.begin(), values.end(), 0 );
(In this particular case, it might be even simpler to just do everything
in the while loop. The manipulators are a generally useful technique,
however, and can be used in a wider context.)
A simple version:
int getIntValue(const std::string& data)
{
stringstream ss(data);
int i=0;
ss >> i;
return i;
}
int getSum(std::vector<std::string>& c)
{
int sum = 0;
for (auto m = c.begin(); m!= c.end(); ++m)
{
sum += getIntValue(*m);
}
return sum;
}
Done