Extracting Numbers from Mixed String using stringstream - c++

I am trying to extract numbers from a string like Hello1234 using stringstream. I have written the code which works for extracting numbers when entered as apart from the string like:
Hello 1234 World 9876 Hello1234
gives 1234 9876 as output
but it doesn't read the mixed string which has both string and number. How can we extract it?
- For example: Hello1234 should give 1234.
Here is my code until now:
cout << "Welcome to the string stream program. " << endl;
string string1;
cout << "Enter a string with numbers and words: ";
getline(cin, string1);
stringstream ss; //intiazling string stream
ss << string1; //stores the string in stringstream
string temp; //string for reading words
int number; //int for reading integers
while(!ss.eof()) {
ss >> temp;
if (stringstream(temp) >> number) {
cout << "A number found is: " << number << endl;
}
}

If you're not limited to a solution that uses std::stringstream, I suggest you take a look at regular expressions. Example:
int main() {
std::string s = "Hello 123 World 456 Hello789";
std::regex regex(R"(\d+)"); // matches a sequence of digits
std::smatch match;
while (std::regex_search(s, match, regex)) {
std::cout << std::stoi(match.str()) << std::endl;
s = match.suffix();
}
}
The output:
123
456
789

Simply replace any alpha characters in the string with white-space before you do the stream extraction.
std::string str = "Hello 1234 World 9876 Hello1234";
for (char& c : str)
{
if (isalpha(c))
c = ' ';
}
std::stringstream ss(str);
int val;
while (ss >> val)
std::cout << val << "\n";
Output:
1234
9876
1234

Question itself is very trivial and as programmer most of us solving this kind of problem everyday. And we know there are many solution for any give problem but as programmer we try to find out best possible for any given problem.
When I came across this question there are already many useful and correct answer, but to satisfy my curiosity I try to benchmark all other solution, to find out best one.
I found best one out of all above, and feel that there is still some room for improvement.
So I am posting here my solution along with benchmark code.
#include <chrono>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
#define REQUIER_EQUAL(x, y) \
if ((x) != (y)) { \
std::cout << __PRETTY_FUNCTION__ << " failed at :" << __LINE__ \
<< std::endl \
<< "\tx:" << (x) << "\ty:" << (y) << std::endl; \
; \
}
#define RUN_FUNCTION(func, in, out) \
auto start = std::chrono::system_clock::now(); \
func(in, out); \
auto stop = std::chrono::system_clock::now(); \
std::cout << "Time in " << __PRETTY_FUNCTION__ << ":" \
<< std::chrono::duration_cast<std::chrono::microseconds>(stop - \
start) \
.count() \
<< " usec" << std::endl;
//Solution by #Evg
void getNumbers1(std::string input, std::vector<int> &output) {
std::regex regex(R"(\d+)"); // matches a sequence of digits
std::smatch match;
while (std::regex_search(input, match, regex)) {
output.push_back(std::stoi(match.str()));
input = match.suffix();
}
}
//Solution by #n314159
void getNumbers2(std::string input, std::vector<int> &output) {
std::stringstream ss;
int number;
for (const char c : input) {
if (std::isdigit(static_cast<unsigned char>(c))) { // Thanks to Aconcagua
ss << c;
} else if (ss >> number) {
output.push_back(number);
}
}
}
//Solution by #The Failure by Design
void getNumbers3(std::string input, std::vector<int> &output) {
istringstream is{input};
char c;
int n;
while (is.get(c)) {
if (!isdigit(static_cast<unsigned char>(c)))
continue;
is.putback(c);
is >> n;
output.push_back(n);
}
}
//Solution by #acraig5075
void getNumbers4(std::string input, std::vector<int> &output) {
for (char &c : input) {
if (isalpha(c))
c = ' ';
}
std::stringstream ss(input);
int val;
while (ss >> val)
output.push_back(val);
}
//Solution by me
void getNumbers5(std::string input, std::vector<int> &output) {
std::size_t start = std::string::npos, stop = std::string::npos;
for (auto i = 0; i < input.size(); ++i) {
if (isdigit(input.at(i))) {
if (start == std::string::npos) {
start = i;
}
} else {
if (start != std::string::npos) {
output.push_back(std::stoi(input.substr(start, i - start)));
start = std::string::npos;
}
}
}
if (start != std::string::npos)
output.push_back(std::stoi(input.substr(start, input.size() - start)));
}
void test1_getNumbers1() {
std::string input = "Hello 123 World 456 Hello789 ";
std::vector<int> output;
RUN_FUNCTION(getNumbers1, input, output);
REQUIER_EQUAL(output.size(), 3);
REQUIER_EQUAL(output[0], 123);
REQUIER_EQUAL(output[1], 456);
REQUIER_EQUAL(output[2], 789);
}
void test1_getNumbers2() {
std::string input = "Hello 123 World 456 Hello789";
std::vector<int> output;
RUN_FUNCTION(getNumbers2, input, output);
REQUIER_EQUAL(output.size(), 3);
REQUIER_EQUAL(output[0], 123);
REQUIER_EQUAL(output[1], 456);
REQUIER_EQUAL(output[2], 789);
}
void test1_getNumbers3() {
std::string input = "Hello 123 World 456 Hello789";
std::vector<int> output;
RUN_FUNCTION(getNumbers3, input, output);
REQUIER_EQUAL(output.size(), 3);
REQUIER_EQUAL(output[0], 123);
REQUIER_EQUAL(output[1], 456);
REQUIER_EQUAL(output[2], 789);
}
void test1_getNumbers4() {
std::string input = "Hello 123 World 456 Hello789";
std::vector<int> output;
RUN_FUNCTION(getNumbers4, input, output);
REQUIER_EQUAL(output.size(), 3);
REQUIER_EQUAL(output[0], 123);
REQUIER_EQUAL(output[1], 456);
REQUIER_EQUAL(output[2], 789);
}
void test1_getNumbers5() {
std::string input = "Hello 123 World 456 Hello789";
std::vector<int> output;
RUN_FUNCTION(getNumbers5, input, output);
REQUIER_EQUAL(output.size(), 3);
REQUIER_EQUAL(output[0], 123);
REQUIER_EQUAL(output[1], 456);
REQUIER_EQUAL(output[2], 789);
}
int main() {
test1_getNumbers1();
// test1_getNumbers2();
test1_getNumbers3();
test1_getNumbers4();
test1_getNumbers5();
return 0;
}
Sample output on my platform
Time in void test1_getNumbers1():703 usec
Time in void test1_getNumbers3():17 usec
Time in void test1_getNumbers4():10 usec
Time in void test1_getNumbers5():6 usec

Adding my version:
#include <iostream>
#include <string>
#include <sstream>
int main(){
std::string s;
std::getline(std::cin, s);
std::stringstream ss;
int number;
for(const char c: s){
if( std::isdigit(static_cast<unsigned char>(c)) ){ //Thanks to Aconcagua
ss << c;
} else if ( ss >> number ) {
std::cout << number << " found\n";
}
ss.clear();
}
if(ss >> number)
{
std::cout << number << " found\n";
}
return 0;
}

You can use the code below with any type of stream - stringstream included. It reads from stream to first digit. The digit is put back in the stream and then the number is read as usually. Live code.
#include <iostream>
using namespace std;
istream& get_number( istream& is, int& n )
{
while ( is && !isdigit( static_cast<unsigned char>( is.get() ) ) )
;
is.unget();
return is >> n;
}
int main()
{
int n;
while ( get_number( cin, n ) )
cout << n << ' ';
}
Notes
Regarding regex - It seems people are forgetting/ignoring the basics and, for some reason (c++ purism?), prefer the sledgehammer for even the most basic problems.
Regarding speed - If you take the stream out of the picture, you cannot beat fundamental c. The code below is tens of times faster than the regex solution and at least a couple of times faster than any answer so far.
const char* get_number( const char*& s, int& n )
{
// end of string
if ( !*s )
return 0;
// skip to first digit
while ( !isdigit( static_cast<unsigned char>( *s ) ) )
++s;
// convert
char* e;
n = strtol( s, &e, 10 );
return s = e;
}
//...
while ( get_number( s, n ) )
//...

Related

C++ Decrease value every time string passes

I'm struggling to find a way to decrease the value in a string every time the string is shown.
Using the code below, consider that the 1st line of the text file is some text #N. #N should be replaced by a number decreasing from 18 to 1. When it reaches 0 it should go back to 18.
#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
void find_and_replace(string & source, string const & find, string const & replace)
{
for (string::size_type i = 0; (i = source.find(find, i)) != string::npos;) {
source.replace(i, find.length(), replace);
i += replace.length();
}
}
int main(int argc, char * argv[])
{
std::ifstream fileIn("Answers.txt", std::ios::in | std::ios::binary);
string question;
string line;
if (!fileIn) {
cout << "Cannot open input file!" << endl;
return 1;
}
while (getline(fileIn, line)) {
if (line == "The answer can be found in a secret place in the woods.") {
fileIn.clear();
fileIn.seekg(0, ios::beg);
}
cout << "Ask a question followed by the Enter key. Or type 'exit' to Exit program.\n";
getline(cin, question);
system("CLS");
find_and_replace(line, "#N", "18");
if (question == "") {
cout << "Your input cannot be blank. Please try again.\n\n";
}
else if (question == "exit")
exit(0);
else {
cout << "Q: " + question
<< "\nA: " + line + "\n\n";
}
}
}
This code only changes #N to 18, nothing more.
Please help guys.
You have hardcoded the value to 18, and you don't have any code which decrements the number.
Try these changes
put this at the start of main
int tempVar=18;
char buffer[100];
and replace
find_and_replace(line, "#N", "18");
with
sprintf(buffer,"%d",tempVar--)
if(tempVar<0)
tempVar=18;
find_and_replace(line, "#N", buffer);
https://www.programiz.com/cpp-programming/library-function/cstdio/sprintf
You can use something like:
#include <sstream>
#include <string>
class Replacer
{
const std::string token_;
const int start_;
int current_;
public:
explicit Replacer(const std::string & token, int start)
: token_(token), start_(start), current_(start)
{
}
std::string replace(const std::string & str)
{
const std::size_t pos = str.find(token_);
if (pos == std::string::npos)
return str;
std::string ret(str);
std::ostringstream oss;
oss << current_;
ret.replace(pos, token_.size(), oss.str());
--current_;
if (current_ == 0)
current_ = start_;
return ret;
}
};
And then you can use it like:
std::string examples[] = {
"",
"nothing",
"some number #N",
"nothing",
"some other #N number",
"nothing",
"#N another test",
"nothing",
};
Replacer replacer("#N", 18);
for (int i = 0; i < 8; ++i)
std::cout << replacer.replace(examples[i]) << '\n';

C++ read file word by word without any symbol

I want to read word by word from a text file. Here's my code in C++:
int main(int argc, const char * argv[]) {
// insert code here...
ifstream file("./wordCount.txt");
string word;
while(file >> word){
cout<<word<<endl;
}
return 0;
}
The text file contains the sentence:
I don't have power, but he has power.
Here's the result I get:
I
don\241\257t
have
power,
but
he
has
power.
Can you tell me how to get the result like the below format:
I
don't
have
power
but
he
has
power
Thanks.
I understand that you're looking for getting rid of the punctuation.
Unfortunately, extracting strings from a stream looks only for spaces as separator. So "don't" or "Hello,world" would be read as one word, and "don' t" or "Hello, world" as two words.
The alternative, is to read the text line by line, and use string::find_first_of() to jump from separator to separator:
string separator{" \t\r\n,.!?;:"};
string line;
string word;
while(getline (cin, line)){ // read line by line
size_t e,s=0; // s = offset of next word, e = end of next word
do {
s = line.find_first_not_of(separator,s); // skip leading separators
if (s==string::npos) // stop if no word left
break;
e=line.find_first_of(separator, s); // find next separator
string word(line.substr(s,e-s)); // construct the word
cout<<word<<endl;
s=e+1; // position after the separator
} while (e!=string::npos); // loop if end of line not reached
}
Online demo
The code below, gets rid of punctuation, except of the apostrophe:
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
using namespace std;
int main(int argc, const char * argv[]) {
ifstream file("wordCount.txt");
string word;
while(file >> word) {
for (auto c : word)
if (ispunct(c) && c != '`')
word.erase(word.find_first_of(c));
cout << word << endl;
}
return 0;
}
should produce the desired output:
Georgioss-MacBook-Pro:~ gsamaras$ g++ -Wall -std=c++0x main.cpp
Georgioss-MacBook-Pro:~ gsamaras$ ./a.out
I
don`t
have
power
but
he
has
power
For the problem with some characters, I encourage you to check the encoding of the file, so try doing (as explained here):
file -I wordCount.txt
wordCount.txt: text/plain; charset=us-ascii
which is what worked for me. Or simply open a text editor and make sure the characters are valid.
To ease debug, I replace the file with std::istringstream.
easy to add additional test input
input is thus documented, and repeatable.
I also added a bool (class data attribute) to simplify enable/disable of additional diagnostic information. (m_dbg)
#include <algorithm>
#include <chrono>
// 'compressed' chrono access --------------vvvvvvv
typedef std::chrono::high_resolution_clock HRClk_t; // std-chrono-hi-res-clk
typedef HRClk_t::time_point Time_t; // std-chrono-hi-res-clk-time-point
typedef std::chrono::microseconds MS_t; // std-chrono-milliseconds
typedef std::chrono::microseconds US_t; // std-chrono-microseconds
typedef std::chrono::nanoseconds NS_t; // std-chrono-nanoseconds
using namespace std::chrono_literals; // support suffixes like 100ms, 2s, 30us
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
class T496_t
{
std::array<char, 256> m_keep;
std::vector<std::string> m_wordVec;
bool m_dbg = false;
public:
T496_t()
{
for (uint i=0; i<256; ++i)
m_keep[i] = static_cast<char>(i);
m_keep[uint(',')] = 0;
m_keep[uint('.')] = 0;
}
~T496_t() = default;
int exec()
{
std::istringstream file(
"Hello\n"
"I don't have power, but he has power.\n"
"I don't have power , but he has power.\n"
); //ifstream file("./wordCount.txt");
uint lineCount = 1;
while(1)
{
std::string line;
(void)std::getline(file, line);
if(file.eof())
{
ltrim(line);
if(0 != line.size())
if(m_dbg) std::cout << __LINE__ << " tail: " << line << std::endl;
break;
}
if(m_dbg) std::cout << "\n line " << lineCount++ << " : '"
<< line << "'\n " << std::setfill('-')
<< std::setw(static_cast<int>(line.size())+12)
<< "-" << std::setfill(' ');
std::cout << '\n';
size_t sz = line.size();
if(0 == sz)
continue; // ignore empty lines
extractWordsFrom(line); // extract words
if(file.eof()) break;
}
return(0);
}
private: // methods
void extractWordsFrom(std::string& unfiltered)
{
std::string line; // filtered
filter(unfiltered, line);
if(0 == line.size()) {
if(m_dbg) std::cout << " empty line" << std::endl; return;
}
size_t indx1 = 0;
do {
while(isspace(line[indx1])) { indx1 += 1; } // skip leading spaces
size_t indx2 = line.find(" ", indx1);
if(std::string::npos == indx2)
{
m_wordVec.push_back(line.substr(indx1));
if(m_dbg) std::cout << " word(" << std::setw(3) << indx1 << ", eoln): ";
std::cout << " " << m_wordVec.back() << std::endl;
break;
}
m_wordVec.push_back(line.substr(indx1, indx2-indx1));
if(m_dbg) std::cout << " word(" << std::setw(3) << indx1 << ","
<< std::setw(3) << indx2 << "): ";
std::cout << " " << m_wordVec.back() << std::endl;
indx1 = indx2+1;
}while(1);
}
void filter(std::string& unfiltered, std::string& line)
{
ltrim(unfiltered); // remove leading blanks
for(uint i=0; i<unfiltered.size(); ++i) // transfer all chars
if(m_keep[unfiltered[i]]) // exception check
line.push_back(unfiltered[i]);
}
// trim from start
void ltrim(std::string &s) {
s.erase(s.begin(),
std::find_if(s.begin(), s.end(),
std::not1(std::ptr_fun<int, int>(std::isspace)) ));
}
// trim from end
void rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(),
std::not1(std::ptr_fun<int, int>(std::isspace))).base(),s.end());
}
// trim from both ends
void lrtrim(std::string &s) { rtrim(s); ltrim(s); }
}; // class T496_t
int main(int /*argc*/, char** /*argv[]*/)
{
setlocale(LC_ALL, "");
std::ios::sync_with_stdio(false);
Time_t start_us = HRClk_t::now();
int retVal = -1;
{
T496_t t496;
retVal = t496.exec();
}
auto duration_us = std::chrono::duration_cast<US_t>(HRClk_t::now() - start_us);
std::cout << "\n\n FINI " << duration_us.count() << " us" << std::endl;
return(retVal);
}
// desired output:
// I
// don't
// have
// power
// but
// he
// has
// power
Output from this code:
Hello
I
don't
have
power
but
he
has
power
I
don't
have
power
but
he
has
power
Output with m_dbg=true
line 1 : 'Hello'
-----------------
word( 0, eoln): Hello
line 2 : 'I don't have power, but he has power.'
-------------------------------------------------
word( 0, 1): I
word( 2, 7): don't
word( 8, 12): have
word( 13, 18): power
word( 19, 22): but
word( 23, 25): he
word( 26, 29): has
word( 30, eoln): power
line 3 : 'I don't have power , but he has power.'
---------------------------------------------------
word( 0, 1): I
word( 2, 7): don't
word( 9, 13): have
word( 14, 19): power
word( 21, 24): but
word( 25, 27): he
word( 28, 31): has
word( 32, eoln): power
FINI 215 us
A simple approach is first to filter the string. Remove any punctuation except apostrophe (i.e. ' ) and replace them with white-space for further manipulation (i.e. to take advantage of some built-in functions).
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <sstream>
#include <iterator>
using namespace std;
bool isOk(char c)
{
if ( ispunct(c) )
if ( c == '\'' )
return false;
return ispunct(c);
}
int main()
{
ifstream file("data.txt");
string word;
while(file >> word){
std::replace_if(word.begin(), word.end(), isOk, ' ');
istringstream ss(word);
copy(istream_iterator<string>(ss), istream_iterator<string>(), ostream_iterator<string>(cout, "\n"));
}
return 0;
}
The output is
I
don't
have
power
but
he
has
power

How to add commas to a string using recursion

I'm a beginner on programming. I'm coding a school assignment and its asking me to add commas to a string using recursion. I have most of it done but when I input a number greater than a million it doesn't add a comma before the first digit. This is what i have so far:
// commas - Convert a number (n) into a string, with commas
string commas(int n) {
ostringstream converted;
converted << n;
string number = converted.str();
int size = number.length();
if (size < 4 )
{
return number;
}
if (size >= 4 )
{
return number.substr(0, number.size() - 3) + "," + number.substr(number.size() - 3, number.length());
}
}
Any help would be greatly appreciated!
The algorithm is fairly simple. It is very similar to your solution except I added the part necessary for recursion. To understand how it works, remove tack_on. Here is example output:
1
10
100
These are the first groups that are returned when the terminating condition is reached (s.size() < 4). Then the rest of the groups are prefixed with a comma and "tacked on". The entire string is built using recursion. This is important because if you left number.substr(0, number.size() - 3) in, your output would look like this:
11,000
1010,000
100100,000
11,0001000,000
I use std::to_string which is C++11:
#include <iostream>
std::string addCommas(int n)
{
std::string s = std::to_string(n);
if (s.size() < 4) return s;
else
{
std::string tack_on = "," + s.substr(s.size() - 3, s.size());
return addCommas(n / 1000) + tack_on;
}
}
You only need to make minimal changes for the C++03/stringstream version:
#include <sstream>
std::ostringstream oss;
std::string addCommas(int n)
{
oss.str(""); // to avoid std::bad_alloc
oss << n;
std::string s = oss.str();
// etc
}
Testing:
int main()
{
std::cout << addCommas(1) << "\n";
std::cout << addCommas(10) << "\n";
std::cout << addCommas(100) << "\n";
std::cout << addCommas(1000) << "\n";
std::cout << addCommas(10000) << "\n";
std::cout << addCommas(100000) << "\n";
std::cout << addCommas(1000000) << "\n";
return 0;
}
Output:
1
10
100
1,000
10,000
100,000
1,000,000
I think this one is a bit simpler and easier to follow:
std::string commas(int n)
{
std::string s = std::to_string(n%1000);
if ((n/1000) == 0) return s;
else
{
// Add zeros if required
while(s.size() < 3)
{
s = "0" + s;
}
return commas(n / 1000) + "," + s;
}
}
an alternative approach without recursion:
class Grouping3 : public std::numpunct< char >
{
protected:
std::string do_grouping() const { return "\003"; }
};
std::string commas( int n )
{
std::ostringstream converted;
converted.imbue( std::locale( converted.getloc(), new Grouping3 ) );
converted << n;
return converted.str();
}
will need #include <locale> in some environments
A possible solution for the assignment could be:
std::string commas( std::string&& str )
{
return str.length() > 3?
commas( str.substr( 0, str.length()-3 ) ) + "," + str.substr( str.length()-3 ):
str;
}
std::string commas( int n )
{
std::ostringstream converted;
converted << n;
return commas( converted.str() );
}

Extract integer from a string

I have string like "y.x-name', where y and x are number ranging from 0 to 100. From this string, what would be the best method to extract 'x' into an integer variable in C++.
You could split the string by . and convert it to integer type directly. The second number in while loop is the one you want, see sample code:
template<typename T>
T stringToDecimal(const string& s)
{
T t = T();
std::stringstream ss(s);
ss >> t;
return t;
}
int func()
{
string s("100.3-name");
std::vector<int> v;
std::stringstream ss(s);
string line;
while(std::getline(ss, line, '.'))
{
v.push_back(stringToDecimal<int>(line));
}
std::cout << v.back() << std::endl;
}
It will output: 3
It seem that this thread has a problem similar to you, it might help ;)
Simple string parsing with C++
You can achieve it with boost::lexical_cast, which utilizes streams like in billz' answer:
Pseudo code would look like this (indices might be wrong in that example):
std::string yxString = "56.74-name";
size_t xStart = yxString.find(".") + 1;
size_t xLength = yxString.find("-") - xStart;
int x = boost::lexical_cast<int>( yxString + xStart, xLength );
Parsing errors can be handled via exceptions that are thrown by lexical_cast.
For more flexible / powerful text matching I suggest boost::regex.
Use two calls to unsigned long strtoul( const char *str, char **str_end, int base ), e.g:
#include <cstdlib>
#include <iostream>
using namespace std;
int main(){
char const * s = "1.99-name";
char *endp;
unsigned long l1 = strtoul(s,&endp,10);
if (endp == s || *endp != '.') {
cerr << "Bad parse" << endl;
return EXIT_FAILURE;
}
s = endp + 1;
unsigned long l2 = strtoul(s,&endp,10);
if (endp == s || *endp != '-') {
cerr << "Bad parse" << endl;
return EXIT_FAILURE;
}
cout << "num 1 = " << l1 << "; num 2 = " << l2 << endl;
return EXIT_FAILURE;
}

extracting last 2 words from a sequence of strings, space-separated

I have any sequence (or sentence) and i want to extract the last 2 strings.
For example,
sdfsdfds sdfs dfsd fgsd 3 dsfds should produce: 3 dsfds
sdfsd (dfgdg)gfdg fg 6 gg should produce: 6 gg
You can use std::string::find_last_of function to find spaces.
int main()
{
std::string test = "sdfsdfds sdfs dfsd fgsd 3 dsfds";
size_t found1 = test.find_last_of( " " );
if ( found1 != string::npos ) {
size_t found2 = test.find_last_of( " ", found1-1 );
if ( found2 != string::npos )
std::cout << test.substr(found2+1, found1-found2-1) << std::endl;
std::cout << test.substr(found1+1) << std::endl;
}
return 0;
}
The following will work if your strings are whitespace separated.
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
int main()
{
string str = "jfdf fhfeif shfowejef dhfojfe";
stringstream sstr(str);
vector<string> vstr;
while(sstr >> str)
{
vstr.push_back(str);
}
if (vstr.size() >= 2)
cout << vstr[vstr.size()-2] << ' ';
if (vstr.size())
cout << vstr[vstr.size()-1] << endl;
return 0;
}
Returns the strings in the wrong order, but if that doesn't matter,
std::string s ("some words here");
std::string::size_type j;
for(int i=0; i<2; ++i) {
if((j = s.find_last_of(' ')) == std::string::npos) {
// there aren't two strings, throw, return, or do something else
return 0;
}
std::cout << s.c_str()+j+1;
s = " " + s.substr(0,j);
}
Alternatively,
struct extract_two_words {
friend std::istream& operator>> (std::istream& in , extract_two_words& etw);
std::string word1;
std::string word2;
};
std::istream& operator>> (std::istream& in , extract_two_words& etw) {
std::string str1, str2;
while(in) {
in >> str1;
in >> str2;
}
etw.word2 = str1;
etw.word1 = str2;
}
I would encourage you to have a look at the Boost library. It has algorithms and data structures that help you tremendously. Here's how to solve your problem using Boost.StringAlgo:
#include <boost/algorithm/string/split.hpp>
#include <iostream>
#include <vector>
#include <string>
int main()
{
std::string test = "sdfsdfds sdfs dfsd fgsd 3 dsfds";
std::vector<std::string> v;
boost::algorithm::split(v, test, [](char c) { return c==' ';});
std::cout << "Second to last: " << v.at(v.size()-2) << std::endl;
std::cout << "Last: " << v.at(v.size()-1) << std::endl;
}
I would also encourage you to always use the vector::at method instead of []. This will give you proper error handling.
int main()
{
std::string test = "sdfsdfds sdfs dfsd fgsd 3 dsfds";
size_t pos = test.length();
for (int i=0; i < 2; i++)
pos = test.find_last_of(" ", pos-1);
std::cout << test.substr(pos+1) << std::endl;
}
Simpler :)