You are here:

C++/Checking if a string contains a number

Advertisement


Question
Hi!

I'm new to C++, but I have some experience with Java.
What is the best way to check if a string contains a standard Double? is there a standard function like isDouble() ?

Context: I'm writing a simple RPN calculator, to teach myself basic C++, and I'm trying to separate operators from numbers in the input stream.

Answer
In short no, not quite like that. For a start the mixed case style in your hypothetical isDouble is not used in the C++ standard library. The C++ standard library, like the C standard library, prefers names to be lower case with words separated by underscore, so if there were such a function it would be more likely to be called is_double.

Note that I am assuming a hosted implementation of ISO/ANSI standard C++ - at least the original 1998 version (there is a new 2011 version). Hosted means having the full C++ standard library as well as other application program features such as using main as an entry point (non-hosted implementation have only a few language support library features and quite possibly do not use main as an entry point - which allows low level system code to be produced using C++).

The logic for parsing numeric or Boolean values in strings is part of the functionality of C++ locales. Locales specify various locale specific things. A locale in full is a collection of facets. There are a number of standard facets, one of which is the num_get facet which takes care of parsing various numeric or Boolean character sequences, returning the resultant value.

Locales are used by IOStreams. The default locale is the C locale, but others may be provided by a C++ library implementation - although the names of any additionally provided locales is NOT standardised - ho hum. However requesting a locale with an empty name string will return a locale representing the current system locale. A stream can be 'imbued' with a locale.

Locales are quite a large topic on their own so I will not go into further details here - you can read up on them further if you are interested.

However, as locales are used by IOStreams the easiest way to invoke numeric parsing of a character sequence is to use formatted input to read into a variable of the required numeric type from an input stream, for example reading straight from the console:

   #include <iostream>
   
   int main()
   {
       double number(0.0);
       
       std::cout << "Enter a number: ";
       std::cin >> number;
   }

Now, streams have various error states such as EOF (end of file) and unrecoverable stream error. One of interest here is a stream failure which usually indicates a formatting error during read - that is what was read was not in the expected format:

   #include <iostream>
   
   int main()
   {
       double number(0.0);
       
       std::cout << "Enter a number: ";
       std::cin >> number;
       if ( std::cin.fail() )
       {
         if ( std::cin.bad() )
         {
         std::cerr << "Standard input stream is in an irrecoverably bad state.\n";
         }
         else // failed, probably because what was entered was not a valid double string.
         {
         std::cerr << "That was NOT a valid number!\n";
         }
       }
       else if ( std::cin.eof() )
       {
         std::cerr << "Unexpected EOF encountered on standard input stream.\n";
       }
       else
       {
         std::cout << " You entered: " << number << "\n";
       }
   }

Note that streams can be good, bad (fatal error state), failed or at end of file. However the fail member function returns true if the stream is bad or failed so we have to check for bad after checking for fail.

We can request exceptions be generated for certain of these conditions (except good or course!). For example we might like std::cin to raise an exception if it enters a bad or EOF state, allowing us to just check for (format) failures in the main line code:

   #include <iostream>
   #include <ios>
   #include <limits>
   
   int main()
   {
       double number(0.0);
       
       std::cin.exceptions( std::ios::badbit | std::ios::eofbit );

       try
       {
         do
         {
         if ( std::cin.fail() )
         {
         std::cerr << "That was NOT a valid number!\n";

         // clear any non-good stream states:
         std::cin.clear();

         // remove any buffered characters up to and including newline:
         std::cin.ignore( std::numeric_limits<int>::max(), '\n' );
         }
         std::cout << "Enter a number: ";
         std::cin >> number;
         }
         while (std::cin.fail());

         std::cout << " You entered: " << number << "\n";
       }
       catch( std::ios::failure const & e )
       {
         std::cerr << "IO error: " << e.what() << "\n";
       }
   }

Note that I am using features of the IOStreams and the C++ library that I am not going to go into too much detail on - you can look up the details in a good C++ library reference such as Nicolai Josuttis' book "The C++ Standard Library A Tutorial and Reference".

This is all very well you are probably thinking but what if the data is already in a string?

Well, we just use a different type of input stream - a std::istringstream, which uses an in-memory sequence of characters to read from rather than the console or file. The in-memory sequence is in the form of a std::string:

   #include <string>
   #include <sstream>
   #include <iostream>
   #include <ios>

   int main()
   {
       double number(0.0);

   // line string could have been read from elsewhere - e.g. the console
       std::string line("1234.234 0.234E2 - 4.33 / 6.33 88.123 + *");

       std::istringstream line_strm(line);

       line_strm.exceptions( std::ios::badbit | std::ios::failbit );

       try
       {
         while ( ! line_strm.eof() )
         {
         std::string token;
         line_strm >> token;
         std::istringstream token_strm(token);
         token_strm >> number;

         // For numeric literal tokens we expect the whole token to be a
         // valid number hence token_strm should be not failed or bad BUT
         // will be at EOF as whole token string should have been processed
         if ( token_strm.eof() && !token_strm.fail() )
         {
         std::cout << "Pushing numeric literal "
         << number << " onto stack...\n";
         }
         else if ( 1==token.length() ) // Operators only single chars
         {
         switch ( token[0] )
         {
         case '+':
         std::cout << "Adding top two stack elements...\n";
         break;

         case '-':
         std::cout << "Subtracting top stack element from 2nd "
         "from top element...\n";
         break;

         case '*':
         std::cout << "Multiplying top two stack elements...\n";
         break;

         case '/':
         std::cout << "Dividing 2nd from top stack element by "
         "top element ...\n";
         break;

         default:
         std::cerr << "Error: Unrecognised operator token: '"
         << token << "'\n";

         // force exit from processing loop
         line_strm.setstate( std::ios::eofbit );
         break;
         }
         }
         else
         {
         std::cerr << "Error: Unrecognised token: '"
         << token << "'\n";

         // force exit from processing loop
         line_strm.setstate( std::ios::eofbit );
         }
         }
       }
       catch( std::ios::failure const & e )
       {
         std::cerr << "IO error: " << e.what() << "\n";
       }
   }

The above program shows a possible bare-bones of a parser for simple RPN like expressions. It is only meant for example purposes and not as a full production quality, tested piece of code so please excuse any short comings, typos, bugs etc. Oh, and it was properly and nicely indented and laid out before posting to All Experts but does assume, like most code, a fixed space font such as Courier. Apologies if the layout got messed up during posting.

As a final hint:

As RPN calculators imply the use of a stack to push, pop, dup etc. values you might like to look into the C++ standard library stack collection adapter as an easy way to obtain a stack of doubles for your program to operate on. In short, #include <stack> and use a std::stack<double>.

Hope this is useful to you and have fun implementing you RPN calculator.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.