How to extract all the different data type numbers in string?

Hi all, I found this topic (http://www.cplusplus.com/forum/general/150052 ) discussed about extracting numbers from string but what if there are numbers from different data types?

e.g.: The total price for 3 apples with 0 defect is $3.20 after 21% discount, which saved $0.8.

Can I extract the numbers 3; 0; 3.20; 21; 0.8; and store them?

This is the code from that topic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main()
{
    string myString;
	vector<int> numbers;
	int num = 0;

	cout << "Enter a string to evaluate: " << endl;
	getline(cin, myString);

	for (int i = 0; i < (int)myString.length(); i++) {
		if (isdigit(myString[i])) {
		    num = (num * 10) + (myString[i] - '0');
		}
		else {
			if (num > 0) {
				numbers.push_back(num);
				num = 0;
			}
		}
	}

	if (num > 0) {
		numbers.push_back(num);
	}
    cout << "Displaying numbers found in string \"" << myString << "\"" << endl;

    for (int i = 0 ; i < (int)numbers.size(); i++) {
        cout << "numbers[" << i << "] = " << numbers[i] << endl;
    }
    
    return 0;
}
Last edited on
There are several numeric conversion functions available for C++ strings, introduced with C++11. For a floating point value std::stof, std::stod or std:stold.
https://en.cppreference.com/w/cpp/string/basic_string/stof

One major "problem" with the C++11 string conversion functions is they throw an exception when they are unable to convert from a string. C++17 introduced a non-throwing function: std::from_chars.
https://en.cppreference.com/w/cpp/utility/from_chars

The newer conversion function is for C strings and is numeric type neutral, you call the same function whether you want an integer or floating point value, the return variable type parameter determines the type returned.
Last edited on
FYI, if you are going to include a link do NOT put parentheses around it; or put a space at the before the closing parenthesis. Without the space the closing parenthesis is assumed to be part of the link, creating a 404.
I always like to put an invisible break between the end of the link and any punctuation.
e.g.
Edit: Credit goes to Duthomhas.

Here's an example using stod like FurryGuy mentioned. It uses try-catch to filter out bad parses, which is generally frowned upon, but the standard library didn't give us something better until C++17 (edit: Actually, the >> extraction from a stringstream can also be used, I forgot about that)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <algorithm>

std::string remove_junk(std::string str)
{
    str.erase(std::remove_if(str.begin(), str.end(),
        [](char ch) { return ch == '$' || ch == '%' || ch == '\''; }),
        str.end());
    return str;
}

std::vector<double> parseNumbers(const std::string& str)
{
    std::vector<double> numbers;
    
    std::istringstream iss(str);
    std::string token;
    while (iss >> token)
    {
        token = remove_junk(token);
        try
        {
            numbers.push_back(std::stod(token));
        }
        catch (...)
        {
            // ignore any errors; don't add to vector
        }
    }
    
    return numbers;
}

int main()
{
    using namespace std;
    cout << "Enter a string to evaluate: " << endl;
    string myString;
    getline(cin, myString);

    std::vector<double> numbers = parseNumbers(myString);

    cout << "Displaying numbers found in string \"" << myString << "\"" << endl;

    for (int i = 0 ; i < (int)numbers.size(); i++) {
        cout << "numbers[" << i << "] = " << numbers[i] << endl;
    }
}


Someone feel free to set up an example using <charconv>, I just don't feel like it.

An issue with this code is that it won't correctly parse something like:
items in a list: 34,39, 45, 98

You could fix this by pre-processing the string to turn any junk characters into spaces.
Last edited on
Another way to convert a C++ string to a number is using a std::stringstream and extract to the desired numeric type. If no number is extracted zero is written to the variable and the failbit is set.
https://en.cppreference.com/w/cpp/io/basic_istream/operator_gtgt
@Ganado, when including links I either put them on separate lines, or put spaces around the link(s). Your method is one I hadn't thought of. Thanks for the nice alternative.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;

vector<double> getNumbers( string str )
{
   vector<double> numbers;
   for ( int p = 0; !str.empty() && p < str.size(); p++ )
   {
      p = str.find_first_of( "0123456789-.", p );
      if ( p == string::npos ) break;
      stringstream ss( str.substr( p ) );
      double x;
      if ( ss >> x  )
      {
         numbers.push_back( x );
         if ( !getline( ss, str ) ) break;
         p = 0;
      }
   }
   return numbers;  
}

int main()
{
   string str;
   //cout << "Enter a string to evaluate: ";
   //getline( cin, str );
   str = "The total price for 3 apples with 0 defect is $3.20 after 21% discount, which saved $0.8";
   vector<double> numbers = getNumbers( str );
   for ( auto e : numbers ) cout << e << " ";
}

3 0 3.2 21 0.8
Last edited on
Why is ‘not’ always spelt ‘NOT’ ?
Hi Ganado, your example works fine but when there is no space in front or behind the number, it could unable to get the same result.

Let's say the string changed to this, it cant get the number, 0.
The total price for 3 apples with defect=0 is $3.20 after 21% discount, which saved $0.8.

But if the string changed to this, it can get the number, 0.
The total price for 3 apples with defect= 0 is $3.20 after 21% discount, which saved $0.8.
Hi lastchance, appreciate your example but I get error on line 33 because it is a range-based for loop. Sorry that I forgot to mention I'm using C++98.

syntax error : missing ',' before ':'

missing type specifier - int assumed. Note: C++ does not support default-int


Any reference for this line to convert it to able to be run on C++98?

for ( auto e : numbers ) cout << e << " ";
@Joshua0101,
I think that C++11 was such a major advance on the first standardised version that it really would be a better idea to upgrade your compiler. However, to avoid range-based loops (and auto):
for ( int e = 0; e < numbers.size(); e++ ) cout << numbers[e] << " ";
The alternative, with iterators, is even longer.
Last edited on
@lastchance,

Yes, I agree with you to use newer compiler. Thanks for the suggestion.

Btw, I found that I make it too complicated to convert the range-based loops. I get the same solution as yours. Anyway, thanks!
for (auto e : numbers) { /* snip */ } is roughly equivalent to:
1
2
3
4
for (std::vector<double>::iterator _it = numbers.begin(), _end = numbers.end(); _it != _end; ++_it) {
    double e = *it;
    /* snip */
}


-Albatross
Last edited on
Can I extract the numbers 3; 0; 3.20; 21; 0.8; and store them?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <sstream>

int main()
{
    std::string str{"3; 0; 3.20; 21; 0.8"};
    std::stringstream iss;
    iss << str;
    int a{};
    int b{};
    double c{};
    int d{};
    double e{};
    
    char dummy;
    
    iss >> a >> dummy >> b >> dummy >> c >> dummy >> d >> dummy >> e;
    
    double sum = a + b + c + d + e;
    
    
    std::cout << a << ' ' << b << ' ' << c << ' ' << d << ' ' << e << '\n';
    std::cout << sum << '\n';
    
    return 0;
}


3 0 3.2 21 0.8
28
Program ended with exit code: 0
AFAIK, std::from_chars() from <charconv> currently only works for real numbers with VS2019.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <string>
#include <vector>
#include <charconv>

std::vector<double> getNumbers(const std::string& str)
{
	using namespace std::string_literals;

	std::vector<double> numbers;

	for (auto first {str.data()}, end {first + str.size()}, ret {first}; (ret = std::find_if(first, end, [nums = "0123456789+-."s](auto ch) {return nums.find(ch) != std::string::npos; })) != end; ) {
		double no {};

		first = std::from_chars(ret, end, no).ptr;
		numbers.push_back(no);
	}

	return numbers;
}

int main()
{
	std::string str;

	//cout << "Enter a string to evaluate: ";
	//getline( cin, str );
	str = "The total price for 3 apples with 0 defect is $3.20 after 21% discount, which saved $0.8";

	const auto numbers {getNumbers(str)};

	for (auto e : numbers)
		std::cout << e << " ";
}



3 0 3.2 21 0.8


However, where from_chars for real numbers is not yet supported, then strtod() is your friend:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>

std::vector<double> getNumbers(const std::string& str)
{
	using namespace std::string_literals;

	std::vector<double> numbers;

	for (auto first {str.data()}, end {first + str.size()}, ret {first}; (ret = std::find_if(first, end, [nums = "0123456789+-."s](auto ch) {return nums.find(ch) != std::string::npos; })) != end; )
		numbers.push_back(std::strtod(ret, const_cast<char**>(&first)));

	return numbers;
}

int main()
{
	std::string str;

	//cout << "Enter a string to evaluate: ";
	//getline( cin, str );
	str = "The total price for 3 apples with 0 defect is $3.20 after 21% discount, which saved $0.8";

	const auto numbers {getNumbers(str)};

	for (auto e : numbers)
		std::cout << e << " ";
}

Last edited on
Topic archived. No new replies allowed.