1

I need help in programming. The program has to accept a string that will eventually be turned to unsigned long. But here's the catch, there must be an error catcher that when you enter combination of hex and symbols like a!!!!!! will produce an error and the unsigned long variable must be able to accept and store the input greater that 4294967295 which is FFFFFFFF. I've tried this code segment:

char buffer[256];
unsigned long holder;
fgets(buffer,256,stdin);
holder = strtoul (buffer,NULL,16);

My problem is that when I enter FFFFFFFFF (9 F's) instead of FFFFFFFF (8 F's), the holder will STILL accept 4294967295 even though its more than the. Another thing is that when I combine both hex and symbols like a!!!!!, the fgets still consider the hex A.

Can you please give me an idea on how to do this? If you know any other idea besides this code, please do let me know. Thanks!

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740

3 Answers3

1

If you are using the "old C-style strings", then you can add extra checks to see that "all characters were taken" by passing a char * to the strtoul call.

In other words:

 char *end_ptr = NULL;
 ....
 errno = 0;
 holder = strtoul(buffer, &end_ptr, 16);

The end_ptr will point at the character one past the accepted input, so if you enter "a!!!!!", it will point at a '!'.

 if (end_ptr != '\0')  // Should point at "end of string marker". 
 {
     ... do something to indicate error. 
 }

To detect overflow, you will have to rely on errno:

if (errno != 0)
{
    ... deal with errors here . 
}

Obviously, you can do:

if (errno != 0 || *end_ptr != '\0')
{
    .... deal with errors. 
}

Using the C++ std:stoul() function will throw an exception, so the C++ style solution would be something like:

try
{
    holder = std::stoul(buffer);
}
catch(...)
{
    ... deal with error ... 
}
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • As far as I can tell if the value is out of range `end_ptr` will still point to a `\0` and you will have to check the actual returned value and `errno`. – Shafik Yaghmour Jul 23 '13 at 15:28
  • @ShafikYaghmour: The descriptions I can find are rather unclear as to exactly what counts as a "invalid character", but you I think you are right. – Mats Petersson Jul 23 '13 at 15:31
  • @Mats Petersson I used the std:stoul() but the problem is that when I enter a!!!, the unsigned long variable turns to 10 which is the equivalent of A... – Christian Mark Ramos Godoy Jul 23 '13 at 15:53
  • Hmm, ok, so you'll probably have to resort to the above trickery with `strtoul` then. Or, use `stringstream` as SteveL explains. – Mats Petersson Jul 23 '13 at 15:54
  • @Mats Petersson OK! I'll try them! Thank you! – Christian Mark Ramos Godoy Jul 23 '13 at 15:57
  • @ChristianMarkRamosGodoy If you pass in the second argument to `stoul` as in my example the value will be `1` but the length of the string is greater than `1` so you know that it could not convert the whole string and you have an issue. – Shafik Yaghmour Jul 23 '13 at 16:32
  • @Shafik Yaghmour The problem with your code is that when I initialize `holder="a!!!!"`, the output still remains 10 which is the value of hex A. – Christian Mark Ramos Godoy Jul 23 '13 at 22:00
  • @ChristianMarkRamosGodoy Did you check the value of `pos` versus the length of the string? You have to do the error checking in order to know if the value returned in valid or not, that is the way it works. Both methods have their trade off you have to pick which one works for you but both require some manual checking on your part, there is no way around that. – Shafik Yaghmour Jul 23 '13 at 22:11
  • I forgot to mention that I am using VS2008. I think `stoul` is not supported. – Christian Mark Ramos Godoy Jul 24 '13 at 00:24
  • That is correct. Either `strtoul` still exists, but not `strtoull`. – Mats Petersson Jul 24 '13 at 00:29
1

So if you look at this document for strtoul you will see this under the Return Value section:

If the converted value falls out of range of corresponding return type, range error occurs and ULONG_MAX or ULLONG_MAX is returned.

So for out of range check you need code similar to this:

if ( ( holder == ULONG_MAX || holder == ULLONG_MAX ) && errno == ERANGE)

For the a!!!! case looking back at the same document, you will see:

The functions sets the pointer pointed to by str_end to point to the character past the last character interpreted. If str_end is NULL, it is ignored.

you are currently passing in NULL but if you pass in an argument:

char *p;
holder = strtoul (buffer,&p,16);

you can now check whether if *p is a NULL terminator and if so then you processed all the characters otherwise you know you had an issue.

You also have the option of using stoul which throw the following exceptions std::invalid_argument if no conversion could be performed and std::out_of_range if the converted value would fall out of the range of the result type.

For example you could do as follows:

std::string str1(n) ;
size_t pos ;

try
{
   holder = std::stoul( str1, &pos, 16 ) ;
}
catch( std::invalid_argument e )
{
    std::cout << "Invalid argument" << std::endl ;
}
catch ( std::out_of_range  e )
{
    std::cout << "Out of range" << std::endl ;
}

pos will be the index of the last character processed, in your case if pos != str1.length() then it could not process the whole string and your have a problem.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
0

As other posters have said - use stoul if you have it.

If you don't, you might be able to do something like:

std::istringstream strm( buffer );

unsigned long holder = 1;
strm >> std::hex >> holder;

if( strm.fail() )
         // out-of-range

if( ! strm.eof() )
         // didn't read all available characters - catches "A!!!"
SteveLove
  • 3,137
  • 15
  • 17