Unpredicted infinite for loop in c++ [duplicate] - c++

This question already has answers here:
How do unsigned integers work
(3 answers)
Closed 2 years ago.
I am writing a program to return first occurrence of the character and the frequency of that character in the string.
For loop in the function is executing infinite times and if condition and block is not executing even once.
What is the problem?
string::size_type find_ch(string &str,char ch,int& i_r)
{
string::size_type first=0;
for(auto i=str.size()-1;i>=0;i--)
{
cout<<"\nInside a for loop."<<endl;
if(str[i]==ch)
{
cout<<"Inside if."<<endl;
first=i+1;
i_r++;
}
}
return first;
}

This loop:
for(auto i = str.size() - 1; i>=0; i--)
will only exit when i is less than 0. But this is not a valid value for an unsigned int. The value will wrap to the maximum unsigned int, and you get an infinite loop.
Note that .size() on a std::string returns a size_t, which is basically an unsigned int type.
One way to fix this would be to cast the return type of .size() to an int, like this:
for(auto i = static_cast<int>(str.size()) - 1; i>=0; i--)
Note that it's important to do the cast before subtracting 1, otherwise you'll get the wrong answer when str is empty.
In c++20, you can avoid this issue entirely by calling the std::ssize() free function, which returns a signed version of the size.

The function definition in general is wrong.
For example if the given character is nit found then why does the function return 0 that is a valid position?
Returning the value first=i+1; will only confuse users of the function. The function shall return std::string::npos if the given character is not found.
Also it is entirely unclear why the loop starts from the end of the string while you need to return the first position of the character.
As for the infinite loop then in the loop there is used variable i that has the unsigned integer type std::string::size_type a value of which never can be negative.
for(auto i=str.size()-1;i>=0;i--)
^^^^^^^^^^^^^^^^^^^
That is the condition i >= 0 is always true by the definition.
The function should be defined the following way
std::pair<std::string::size_type, std::string::size_type> find_ch( const std::string &str, char ch )
{
auto n = str.find( ch );
std::pair<std::string::size_type, std::string::size_type> p( n, 0 );
if ( n != std::string::npos )
{
++p.second;
while ( ( n = str.find( ch, n + 1 ) ) != std::string::npos ) ++p.second;
}
return p;
}
Here is a demonstrative program.
#include <iostream>
#include <string>
#include <utility>
std::pair<std::string::size_type, std::string::size_type> find_ch( const std::string &str, char ch )
{
auto n = str.find( ch );
std::pair<std::string::size_type, std::string::size_type> p( n, 0 );
if ( n != std::string::npos )
{
++p.second;
while ( ( n = str.find( ch, n + 1 ) ) != std::string::npos ) ++p.second;
}
return p;
}
int main()
{
std::string s( "C++ is not the same as C" );
auto p = find_ch( s, 'C' );
if ( p.first != std::string::npos )
{
std::cout << p.first << ": " << p.second << '\n';
}
return 0;
}
The program output is
0: 2
If you are not allowed to use methods of the class std::string then just substitute calls of the method find in the function above to while loops as it is shown below.
#include <iostream>
#include <string>
#include <utility>
std::pair<std::string::size_type, std::string::size_type> find_ch( const std::string &str, char ch )
{
std::pair<std::string::size_type, std::string::size_type> p( std::string::npos, 0 );
std::string::size_type n = 0;
while ( n < str.size() && str[n] != ch ) ++n;
if ( n != str.size() )
{
p.first = n;
++p.second;
while ( ++n != str.size() )
{
if( str[n] == ch ) ++p.second;
}
}
return p;
}
int main()
{
std::string s( "C++ is not the same as C" );
auto p = find_ch( s, 'C' );
if ( p.first != std::string::npos )
{
std::cout << p.first << ": " << p.second << '\n';
}
return 0;
}

Here is an answer similar to #Vlad From Moscow, but uses string functions, and the algorithm std::count.
#include <algorithm>
#include <string>
#include <utility>
#include <iostream>
std::pair<int,int> find_ch(const std::string &str, char ch)
{
std::pair<int, int> ret;
auto pos = str.find_first_of(ch);
if ( pos == std::string::npos )
return {-1,0}; // not found
return { pos, std::count(str.begin() + pos, str.end(), ch) };
}
int main()
{
auto pr = find_ch("abccabc", 'b');
std::cout << "character b is at position " << pr.first << ". Character count is " << pr.second << "\n";
pr = find_ch("abccabc", 'c');
std::cout << "character c is at position " << pr.first << ". Character count is " << pr.second;
}
Output:
character b is at position 1. Character count is 2
character c is at position 2. Character count is 3
Each line of the function basically describes what is being done:
find_first_of the character in the string. If found then return that position and the std::count of that character starting at the first occurrence.
Note the brevity and self-documented way the function is written. A C++ programmer could look at that code and immediately know what it does, due to the names of the functions that are being called.
Writing loops going backwards (as you originally did) with variables incremented here and there, the programmer has to sit down and go through the code to figure out what it is doing, and what the purpose of the function is.

Related

Counting words in an input string in C++ **with consideration for typos

I've been looking for ways to count the number of words in a string, but specifically for strings that may contain typos (i.e. "_This_is_a___test" as opposed to "This_is_a_test"). Most of the pages I've looked at only handle single spaces.
This is actually my first time programming in C++, and I don't have much other programming experience to speak of (2 years of college in C and Java). Although what I have is functional, I'm also aware it's complex, and I'm wondering if there is a more efficient way to achieve the same results?
This is what I have currently. Before I run the string through numWords(), I run it through a trim function that removes leading whitespace, then check that there are still characters remaining.
int numWords(string str) {
int count = 1;
for (int i = 0; i < str.size(); i++) {
if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') {
bool repeat = true;
int j = 1;
while (j < (str.size() - i) && repeat) {
if (str[i + j] != ' ' && str[i + j] != '\t' && str[i + j] != '\n') {
repeat = false;
i = i + j;
count++;
}
else
j++;
}
}
}
return count;
}
Also, I wrote mine to take a string argument, but most of the examples I've seen used (char* str) instead, which I wasn't sure how to use with my input string.
You don't need all those stringstreams to count word boundary
#include <string>
#include <cctype>
int numWords(std::string str)
{
bool space = true; // not in word
int count = 0;
for(auto c:str){
if(std::isspace(c))space=true;
else{
if(space)++count;
space=false;
}
}
return count;
}
One solution is to utilize std::istringstream to count the number of words and to skip over spaces automatically.
#include <sstream>
#include <string>
#include <iostream>
int numWords(std::string str)
{
int count = 0;
std::istringstream strm(str);
std::string word;
while (strm >> word)
++count;
return count;
}
int main()
{
std::cout << numWords(" This is a test ");
}
Output:
4
Albeit as mentioned std::istringstream is more "heavier" in terms of performance than writing your own loop.
Sam's comment made me write a function that does not allocate strings for words. But just creates string_views on the input string.
#include <cassert>
#include <cctype>
#include <vector>
#include <string_view>
#include <iostream>
std::vector<std::string_view> get_words(const std::string& input)
{
std::vector<std::string_view> words;
// the first word begins at an alpha character
auto begin_of_word = std::find_if(input.begin(), input.end(), [](const char c) { return std::isalpha(c); });
auto end_of_word = input.begin();
auto end_of_input = input.end();
// parse the whole string
while (end_of_word != end_of_input)
{
// as long as you see text characters move end_of_word one back
while ((end_of_word != end_of_input) && std::isalpha(*end_of_word)) end_of_word++;
// create a string view from begin of word to end of word.
// no new string memory will be allocated
// std::vector will do some dynamic memory allocation to store string_view (metadata of word positions)
words.emplace_back(begin_of_word, end_of_word);
// then skip all non readable characters.
while ((end_of_word != end_of_input) && !std::isalpha(*end_of_word) ) end_of_word++;
// and if we haven't reached the end then we are at the beginning of a new word.
if ( end_of_word != input.end()) begin_of_word = end_of_word;
}
return words;
}
int main()
{
std::string input{ "This, this is a test!" };
auto words = get_words(input);
for (const auto& word : words)
{
std::cout << word << "\n";
}
return 0;
}
You can use standard function std::distance with std::istringstream the following way
#include <iostream>
#include <sstream>
#include <string>
#include <iterator>
int main()
{
std::string s( " This is a test" );
std::istringstream iss( s );
auto count = std::distance( std::istream_iterator<std::string>( iss ),
std::istream_iterator<std::string>() );
std::cout << count << '\n';
}
The program output is
4
If you want you can place the call of std::distance in a separate function like
#include <iostream>
#include <sstream>
#include <string>
#include <iterator>
size_t numWords( const std::string &s )
{
std::istringstream iss( s );
return std::distance( std::istream_iterator<std::string>( iss ),
std::istream_iterator<std::string>() );
}
int main()
{
std::string s( " This is a test" );
std::cout << numWords( s ) << '\n';
}
If separators can include other characters apart from white space characters as for example punctuations then you should use methods of the class std::string or std::string_view find_first_of and find_first_not_of.
Here is a demonstration program.
#include <iostream>
#include <string>
#include <string_view>
size_t numWords( const std::string_view s, std::string_view delim = " \t" )
{
size_t count = 0;
for ( std::string_view::size_type pos = 0;
( pos = s.find_first_not_of( delim, pos ) ) != std::string_view::npos;
pos = s.find_first_of( delim, pos ) )
{
++count;
}
return count;
}
int main()
{
std::string s( "Is it a test ? Yes ! Now we will run it ..." );
std::cout << numWords( s, " \t!?.," ) << '\n';
}
The program output is
10
you can do it easily with regex
int numWords(std::string str)
{
std::regex re("\\S+"); // or `[^ \t\n]+` to exactly match the question
return std::distance(
std::sregex_iterator(str.begin(), str.end(), re),
std::sregex_iterator()
);
}

STL map to find the first non repeatable character

I have written a small C++ program to keep a count of the alphabets.
I am using stl map for the same,
Interestingly, i am not getting the list as it appeared in the input.
For example for the word TESTER, my program should give
T 2
E 2
S 1
R 1
But its giving,
E 2
R 1
S 1
T 2
change in the position of the alphabets,
I want the o/p of the alphabets as it appeared in the input. Please help me if i am missing anything.Here is my code
#include<iostream>
#include<map>
using namespace std;
int main()
{
char *str = "TESTER";
map<char,int> checkmap;
map<char,int>::iterator p;
int i;
while( *str != '\0' )
{
p = checkmap.find(*str);
i = p->second;
if(p == checkmap.end())
{
checkmap.insert(std::make_pair(*str,++i));
}
else
{
p->second = ++(p->second);
}
str++;
}
for(p=checkmap.begin(); p!=checkmap.end(); p++)
{
/*if(p->second == 1)
{
cout<<(*p).first<<endl;
}*/
cout<<p->first<<"\t"<<p->second<<endl;
}
return 0;
}
Here is shown an approach how it can be done
#include <iostream>
#include <map>
#include <cstring>
int main()
{
const char *str = "TESTER";
auto order = [&]( char c1, char c2 )
{
return ( std::strchr( str, c1 ) < std::strchr( str, c2 ) );
};
std::map<char, int, decltype( order )> m( order );
for ( const char *p = str; *p; ++p ) ++m[*p];
for ( const auto &p : m ) std::cout << p.first << ' ' << p.second << std::endl;
std::cout << std::endl;
return 0;
}
The program output is
T 2
E 2
S 1
R 1
You're missing that std::map has its own internal ordering, which is completely independent of the order in which elements are added. As you can see from your example, it is ordered alphabetically. This is in increasing order of the value of the char key.
Also note that your map manipulations are overly complex. All you need to do is
char *str = "TESTER";
map<char,int> checkmap;
while( *str != '\0' )
{
checkmap[*str]++;
++str;
}
The while can be collapsed further if you're into that kind of thing:
while( *str != '\0' ) checkmap[*str++]++;
For the general problem of mapping values while maintaining insertion order, see A std::map that keep track of the order of insertion?
There is no way to keep track of the order in which elements are added to map. To get the same order it would be advisable to use std::vector<std::char, int> and then update the same.

c++ string iterator "find first of"

Is there a method in string iterator like find_first_of on string ?
Something like:
string::iterator it;
string str(" h asdasf ^& saafa");
it = FIND_FIRST_OF("&az^");
std::cout << *it << std::endl;
And the result:
a
You can do it indirectly
auto pos = str.find_first_of("&az^");
then advance the iterator
if(pos != std::string::npos) // thanks to #Mike Seymour
std::advance(it, pos);
I guess you can also do some kind of std::find with a lambda, but the above is really much more simpler and concise.
I think std::find_first_of is what you're looking for.
string::iterator it;
string str(" h asdasf ^& saafa");
string find_me ("&az^");
it = std::find_first_of (str.begin(), str.end(), find_me.begin(), find_me.end());
std::cout << *it << std::endl;
I would write a function to clean up the overhead involved in constructing/using the intermediary find_me variable if using this method with any frequency.
Try this:
std::string::size_type position = example.find_first_of(".");
if (position != std::string::npos)
{
std::advance(string_iterator, position);
}
else
{
string_iterator = example.end();
}
Class std::string has its own methods find_first_of and find_last_of apart from other find methods.
Here is a demonstrative program
#include <iostream>
#include <string>
int main()
{
std::string s( " h asdasf ^& saafa" );
auto pos = s.find_first_of( "&az^" );
if ( pos != std::string::npos ) std::cout << s[pos] << std::endl;
pos = s.find_last_of( "&az^" );
if ( pos != std::string::npos ) std::cout << s[pos] << std::endl;
return 0;
}
The program output is
a
a
Here is another demonstrative program that finds all characters in the string that are specified in the character literal
#include <iostream>
#include <string>
int main()
{
std::string s( " h asdasf ^& saafa" );
for ( std::string::size_type pos = 0;
( pos = s.find_first_of( "&az^", pos ) ) != std::string::npos;
++pos )
{
std::cout << pos << ": " << s[pos] << std::endl;
}
return 0;
}
The program output is
4: a
7: a
11: ^
12: &
15: a
16: a
18: a
Knowing the found position you always can get the corresponding iterator in the object:
std::string::iterator it = std::next( s.begin(), pos );
or
auto it = std::next( s.begin(), pos );
or simply
std::string::iterator it = s.begin() + pos;
Also there is standard algorithm std::find_first_of declared in header <algorithm> that also can be used with objects of type std::string.

Addition on user-defined types C++

I am writing my own class(called "Longer") such that it can hold a number without any upper bound unlike int. I am using std::string for this.
I am having problem on performing addition.
If i simply add two string, i can't get correct result.
I thought of converting string to int and then performing addition,
but long string can't be converted to int.
How can i define my own way of adding two strings so that i get the desired result? Here is the code:
Longer.h
#pragma once
#include <string>
class Longer
{
public:
Longer(std::string number);
Longer add(Longer num2);
void print();
private:
std::string number;
};
Longer.cpp
#include "Longer.h"
#include <iostream>
#include <string>
Longer::Longer(std::string num): number(num)
{
}
Longer Longer::add(Longer num2){
return Longer(number+num2.number);
}
void Longer::print(){
std::cout<<number<<"\n";
}
main.cpp
#include <iostream>
#include "Longer.h"
int main(){
Longer num1("123456789101112");
Longer num2("121110987654321");
Longer num3 = num1.add(num2);
num3.print();
}
I don't wonder addition doesn't work like you intended. std::string is not meant to be used as an arbitrary-long number container, that's why.
You must define your own way to "add" two strings, which should consist into iterating backwards both strings (from the end) and compare single characters, by interpreting them as numbers.
without any upper bound unlike int
Be careful with such things. There will always be some upper bound with any solution, at the very least when your machine's memory is exhausted. A robust application should always have some kind of error checking.
If i simply add two string, i can't get correct result.
Well, that's obvious, isn't it? String concatentation doesn't know anything about mathematical semantics.
I thought of converting string to int and then performing addition,
but long string can't be converted to int.
Exactly. Internally converting the string to a built-in type would defeat the whole purpose of the solution.
How can i define my own way of adding two strings so that i get the
desired result?
The goal is apparently to support numbers bigger than what the built-in types provide.
First of all, are you really sure that your application needs to work with such huge numbers? Even a standard int should usually be more than enough, not to mention long long (standard since C++11 but practically usable even before that).
Perhaps what you really need is to detect invalid user input like "10000000000000000000000000".
String streams provide this error detection for you. Here is a complete example for you to play with, including exemplary usage of std::numeric_limits:
#include <iostream>
#include <stdexcept>
#include <exception>
#include <limits>
int ReadInt()
{
int result;
std::cin >> result;
if (!std::cin)
{
throw std::runtime_error("Illegal number");
}
return result;
}
int main()
{
try
{
std::cout << "Enter number (max: " << std::numeric_limits<int>::max() << ") > ";
int input = ReadInt();
std::cout << "You entered the following number: " << input << "\n";
}
catch (std::exception const &exc)
{
std::cerr << exc.what() << "\n";
}
}
Here are three example runs on my machine. The first with a "normal" small number, the second just barely larger than the maximum possible, the third exactly the largest possible integer:
Enter number (max: 2147483647) > 1000
You entered the following number: 1000
Enter number (max: 2147483647) > 2147483648
Illegal number
Enter number (max: 2147483647) > 2147483647
You entered the following number: 2147483647
Now, if you really really must support large integer numbers internally, don't reinvent the wheel. Use Boost.Multiprecision:
http://www.boost.org/doc/libs/1_55_0/libs/multiprecision/doc/html/index.html
Since the documentation of that particular library may be a bit hard to swallow, here is an ultra-simple example to get you started:
#include <iostream>
#include <stdexcept>
#include <exception>
#include <boost/multiprecision/cpp_int.hpp>
int main()
{
try
{
boost::multiprecision::int128_t number("100000000000000000000000000000000");
number *= 2;
std::cout << number << "\n";
}
catch (std::exception const &exc)
{
std::cerr << exc.what() << "\n";
}
}
This actually prints 200000000000000000000000000000000.
#include <iostream>
using namespace std;
class Longer {
public:
Longer(std::string number): number(number) {}
void print() { cout << number << endl; }
Longer add(Longer num2) {
char over = '0'; string it;
for(int i = number.size() - 1,
j = num2.number.size() - 1;
i >= 0 || j >= 0; i--, j--) {
char one = i >= 0 ? number[i] : '0';
char two = j >= 0 ? num2.number[j] : '0';
char dig = one-'0' + two-'0' + over;
over = '0'; if(dig > '9') {
dig -= 10; over = '1'; }
it.insert(0, 1, dig);
}
if(over != '0') it.insert(0, 1, over);
return Longer(it);
}
private:
std::string number;
};
int main() {
Longer num1("123456789101112"); num1.print();
Longer num2("121110987654321"); num2.print();
Longer num3 = num1.add(num2); num3.print();
}
Output:
123456789101112
121110987654321
244567776755433
But if that was not homework, look at boost::multiprecision::cpp_int
Here is a ready to use solution
#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
#include <iterator>
class Longer
{
public:
Longer() : value( 1, '0' ) {}
Longer (std::string s )
: value( s.rbegin(), s.rend() )
{}
Longer( const char *s )
: value( std::reverse_iterator<const char *>( s + std::strlen( s ) ),
std::reverse_iterator<const char *>( s ) )
{}
const Longer add( const Longer &number ) const;
void print( std::ostream &os = std::cout ) const
{
os << std::string( value.rbegin(), value.rend() );
}
private:
std::string value;
};
const Longer Longer::add( const Longer &number ) const
{
std::pair<std::string::size_type, std::string::size_type> sizes =
std::minmax( this->value.size(), number.value.size() );
std::string result;
result.reserve( sizes.second + 1 );
int overflow = 0;
auto out = std::transform( this->value.begin(),
std::next( this->value.begin(), sizes.first ),
number.value.begin(),
std::back_inserter( result ),
[&] ( char c1, char c2 ) ->char
{
char c = ( c1 - '0' ) + ( c2 -'0' ) + overflow;
overflow = c / 10;
return c % 10 + '0';
} );
std::string::const_iterator first, last;
if ( this->value.size() < number.value.size() )
{
first = std::next( number.value.begin(), sizes.first );
last = number.value.end();
}
else
{
first = std::next( this->value.begin(), sizes.first );
last = this->value.end();
}
std::transform(first, last, out,
[&]( char c )
{
return ( c = c - '0' + overflow ),
( overflow = c / 10 ),
( c % 10 + '0' );
} );
if ( overflow ) result.push_back( overflow + '0' );
Longer n;
n.value = result;
return n;
}
int main()
{
Longer n1( "12345678912345678" );
n1.print();
std::cout << std::endl;
Longer n2( "1123" );
n2.print();
std::cout << std::endl;
Longer n3 = n2.add( "877" );
n3.print();
std::cout << std::endl;
Longer n4( "9999999999" );
n4.print();
std::cout << std::endl;
Longer n5 = n4.add( "1" );
n5.print();
std::cout << std::endl;
return 0;
}
The output is
12345678912345678
1123
2000
9999999999
10000000000
Take into account that it is more convinient to store the string in the reverse order inside the class.

how to extract integer from a string? c++

i have this line taken from a txt file (first line in the file):
#operation=1(create circle and add to picture) name X Y radius.
why does this code doesnt take the integer 1 and puts it into k?
Circle Circle::CreateCirc(Circle c){
int k;
ifstream myfile("cmd.txt");
if (!myfile.is_open())
cout<<"Unable to open the requested file "<<endl;
string line,line2="create circle";
for (int i=1;i<countrows();i++)
{
getline(myfile,line);
if (line.find(line2)!=string::npos)
{
istringstream ss(line);
ss>>k;
cout<<k<<endl;
}
}
return c;
}
instead im getting adress memory...help plz
Because the line doesn't start with a number. You'll need to skip over the #operation= part before extracting a number.
You should check the result of the extraction, and of getline, to help identify what's going wrong when these fail.
Also, if countrows() returns the expected number of rows in the file, then your loop would miss out the last one. Either loop from zero, or while i <= countrows(); or, if you want to process every line in the file, you could simply loop while (getline(myfile,line)).
If the actual text in the file you try to read starts with "#operation=1" and you want the number 1 from that, you can't use the simple input operator. It will read the character '#' first, which isn't a digit and so the parsing will fail and k will not be initialized. And if k is not initialized, it will be of indeterminate value, and reading that value will lead to undefined behavior and seemingly random output.
You need to check that the extraction worked:
if (ss >> k)
std::cout << k << '\n';
That won't solve your problem though, as like I said above, you can't use the simple input operator here. You need to parse the string using other methods. One way might be to find the equal character '=' and get a sub-string after that to try and extract the number.
try this:
Circle Circle::CreateCirc(Circle c){
const std::streamsize ALL = std::numeric_limits< std::streamsize >::max(); // #include <limits> needed
int k;
ifstream myfile("cmd.txt");
if (!myfile.is_open())
cout<<"Unable to open the requested file "<<endl;
for (int i=1;i<countrows(); ++i, myfile.ignore(ALL,'\n') ) // skip rest of the line
{
if( myfile.ignore(ALL,'=') >> k )
{
cout<<k<<endl;
}
else
break; // read error
}
return c;
}
EDIT: A way to do it not much bit a little closer to the way you were trying to do it using atoi() rather than streams.
#include <iostream>
#include <cstdlib> // for atoi()
int main(){
std::string str = "#operation=1(create circle and add to picture) name X Y radius.";
int k;
std::string line=str, line2="(create circle";
std::size_t fnd = line.find(line2);
if (fnd!=std::string::npos)
{
k = atoi(&str[fnd-1]); // int atoi(const char *str) == argument to integer
std::cout<< k << " " << str[fnd-1] << str[fnd] << " ";
}
}
There are a few ways to extract an integer from a string but i like to filter out the digit from the string;
#include <iostream>
int main(){
std::string str = "#operation=1(create circle and add to picture) name X Y radius.";
int k = 0;
// an array of our base10 digits to filter through and compare
const char digit[] = {'0','1','2','3','4','5','6','7','8','9'};
for(int s_filter = 0; s_filter<str.size(); ++s_filter){
for(int d_filter = 0; d_filter<10; ++d_filter){
// filter through each char in string and
// also filter through each digit before the next char
if(digit[d_filter] == str[s_filter]) {
// if so the char is equal to one of our digits
k = d_filter;// and d_filter is equal to our digit
break;
} else continue;
}
}
switch(k) {
case 1:
std::cout<< "k == 1";
// do stuff for operation 1..
return 0;
case 2:
std::cout<< "k != 1";
// do more stuff
break;
//case 3: ..etc.. etc..
default:
std::cout<< "not a digit";
return 1;
}
}
// find_num.cpp (cX) 2015 adolfo.dimare#gmail.com
// http://stackoverflow.com/questions/21115457/
#include <string> // std::string
#include <cctype> // isnum
/// Find the number in 'str' starting at position 'pos'.
/// Returns the position of the first digit of the number.
/// Returns std::string::npos when no further numbers appear within 'str'.
/// Returns std::string::npos when 'pos >= str.length()'.
size_t find_num( const std::string str, size_t pos ) {
size_t len = str.length();
bool isNegative = false;
while ( pos < len ) {
if ( isdigit(str[pos]) ) {
return ( isNegative ? pos-1 : pos );
}
else if ( str[pos]=='-' ) {
isNegative = true;
}
else {
isNegative = false;
}
++pos;
}
return std::string::npos;
}
#include <cassert> // assert()
#include <cstring> // strlen();
int main() {
std::string str;
str = "";
assert( std::string::npos == find_num( str, 0 ) );
assert( std::string::npos == find_num( str, 9 ) );
str = "#operation=1(create circle and add to picture) name X Y radius.";
assert( strlen("#operation=") == find_num( str, 0 ) );
str = "abcd 111 xyx 12.33 alpha 345.12e-23";
/// 0123456789.123456789.123456789.123456789.
assert( 5 == find_num( str, 0 ) );
assert( 13 == find_num( str, 5+3 ) );
assert( 25 == find_num( str, 20 ) );
str = "abcd-111 xyx-12.33 alpha-345.12e-23";
/// 0123456789.123456789.123456789.123456789.
assert( 4 == find_num( str, 0 ) );
assert( 12 == find_num( str, 5+3 ) );
assert( 24 == find_num( str, 20 ) );
str = "-1";
assert( 0 == find_num( str, 0 ) );
assert( 1 == find_num( str, 1 ) );
assert( std::string::npos == find_num( str, 2 ) );
assert( std::string::npos == find_num( str, strlen("-1") ) );
return 0;
}