help-gplusplus
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: How do I convert and validate numeric data to a string object?


From: Thomas Maeder
Subject: Re: How do I convert and validate numeric data to a string object?
Date: Thu, 14 Feb 2008 17:14:46 +0100
User-agent: Gnus/5.1006 (Gnus v5.10.6) XEmacs/21.4 (Jumbo Shrimp, linux)

Pep <pepaltavista@yahoo.co.uk> writes:

> Thomas Maeder wrote:
>
>> Pep <pepaltavista@yahoo.co.uk> writes:
>>
>> > I have numeric input which I must convert to strings and validate
>> > whilst doing the conversion. I do not want to mix C functions like
>> > strtol in to my c++ code, so I'm trying to implement a C++ method
>> > using examples found in google but none of them work :(
>>
>> std::strtol() is a Standard C++ function. Where's the problem?
>>
>>
>
> lol, sorry I was thinking of the likes of atol there, I had tried
> strtol but that did not work either, so I discounted it.
>
> <snip>
>
> Hmm, I was a bit scratchy with my cut & paste there :(
>
> Here's the real working code, followed by actual test results. I have
> removed the resetting of the istringstream as that is clearly a bug
> which is documented and there is a work around for it.
>
> I'm using g++ 3.4.6 on Debian GNU/Linux 4.0 r1 _Etch_
>
> One point of note about the source is that you said I needed to
> include the <istream> and <ostream> headers but they are not needed
> in this code. The only header needed for the streams is the
> <sstream> header, so that is possibly an incompatibility between our
> compilers?

I haven't seen a release of gcc that requires us to #include <istream>
or <ostream>.

What I mentioned is what the Standard "says"; an implementation is
free to (e.g.) #include <istream> and <ostream> from <iostream> so
that code compiles which just #includes <iostream> but not the other
headers. There's a risk that such code is not portable.


The code you posted now was much more useful. I have varied it to:

#include <cassert>
#include <sstream>
#include <iostream>
#include <istream>
#include <ostream>

int main(int argc, char** argv)
{
  assert(argc>0);
  
  std::istringstream iss(argv[1]);
  iss.exceptions(std::istringstream::eofbit
                 | std::istringstream::failbit
                 | std::istringstream::badbit);

  long testLong(0);
  try
  {
    iss >> testLong;
  }
  catch(std::istringstream::failure& e)
  {
    std::cout << "exception: [" << e.what() << "]\n";
    if (iss.eof())
      std::cout << "eof\n";
    if (iss.bad())
      std::cout << "bad\n";
    if (!iss)
      std::cout << "fail\n";
  }

  std::cout << "testLong [" << testLong << "]\n";

  return EXIT_SUCCESS;
}

partly to adapt it to my "handwriting", and also to get more
information about iss's state when the exception is caught.


> test results
>
> // test 12389
> ./test 12389
> caught a exception geezer! [basic_ios::clear]
> testLong [12389]

I now get:

% ./a.out  12389
exception: [basic_ios::clear]
eof
testLong [12389]

, which, as I mentioned in my previous post, doesn't surprise me. eof
is reached while digits are consumed from iss, and since you asked for
an exception to be thrown when eof is reached, that's what you get.


If I remove eofbit from the iss.exceptions() call, I get:

% ./a.out  12389
testLong [12389]


> NOTE: I do not expect this to throw an exception

I did :-) I wouldn't e.what() to return the not very helpful
"basic_ios::clear", but any return value is correct.


> // test A12389
> ./test A12389
> caught a exception geezer! [basic_ios::clear]
> conversion was illegal geezer!
> testLong [-1210077196]

% ./a.out A12389
exception: [basic_ios::clear]
fail
testLong [0]

> NOTE: I expect this to throw an exception but how can I trust it as
> it is the same exception that was thrown in "test 12389", where I
> did not expect to throw an exception.

We must not rely on the return value of what().


> // test 123A89
> ./test 123A89
> testLong [123]

% ./a.out 123A89
testLong [123]

> NOTE: well this certainly does not work how I expect it to :(

I'm afraid that you have to modify your expectations here. If you want
to check whether the entire input can be converted to a long, you can
check for eof() after the conversion (see below).


> //test 12389A
> ./test 12389A
> testLong [12389]
>
> NOTE: I am expecting to see both the exception error message and the
> illegal conversion error message here.

Same as above.


> In short I am expecting to see the exception and the illegal
> conversion error messages for all but the first test "test
> 12389". So what am I misunderstanding here?

The requirements on operator>> imposed by the Standard.


> It is looking like I will have to write a function that iterates the
> string checking if each character is a digit before I use the
> istringstream or strtol to convert the string to a long because they
> do not seem to be able to validate the input themselves.

I wouldn't.


FWIW, I have never used the exceptions() member function of a stream
object. I suggest that you use one of the following alternatives:

int main(int argc, char** argv)
{
  assert(argc>0);
  
  std::istringstream iss(argv[1]);

  long testLong(0);
  if (iss >> testLong && iss.eof())
    std::cout << "testLong [" << testLong << "]\n";
  else
    std::cout << "conversion failed\n";
}


int main(int argc, char** argv)
{
  assert(argc>0);

  char *end;
  long testLong(std::strtol(argv[1],&end,10));
  if (*end==0)
    std::cout << "testLong [" << testLong << "]\n";
  else
    std::cout << "conversion failed\n";
}


int main(int argc, char** argv)
{
  assert(argc>0);

  try
  {
    long testLong(boost::lexical_cast<long>(argv[1]));
    std::cout << "testLong [" << testLong << "]\n";
  }
  catch (boost::bad_lexical_cast const &)
  {
    std::cout << "conversion failed\n";
  }
}

(cf. http://www.boost.org/libs/conversion/lexical_cast.htm)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]