0

I have a doubt with conversion from string to int. I got a string through function fgets then I used strtol function to convert it to int. This is the code :

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <errno.h>
#include <limits.h>

int main(void)
{
  char buf[BUFSIZ];
  char *p = NULL;
  long int val;
  int numero;
  int temp;

  do
  {
temp=0;
printf ("Enter a number: ");
if (fgets(buf, sizeof(buf), stdin) != NULL)
{
    val = strtol(buf, &p, 10);
    if(buf==p)
    {
        printf(" no digits \n");
        temp=1;
    }
    if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val   == 0)) 
    {
        perror("strtol");
        temp=1;
    }
    if (*p != '\0')
    {
        printf("you have insert any char character \n");
        temp=1;
    }
 }
else
 {
    printf("Error\n");
    temp=1;
 }   
}
while(temp == 1);

 /* If we got here, strtol() successfully parsed a number */
 numero=(int)val;
 printf("***** The number is : %d ******* \n",numero);

return 0;
}

and this code doen't work, but it work if I replace this control

if (*p != '\0')
    {
        printf("you have insert any char character \n");
        temp=1;
    }

with this one :

if (*p != '\n')
    {
        printf("you have insert any char character \n");
        temp=1;
    }

Do you know why ? :)

EDIT : This is the code of my final function version :) Thanks to all :) Now it seem that all correctly works :

int readIN(int *numero)  
{
   long int val;
   char buf[BUFSIZ];
   char *p = NULL;

   if (fgets(buf, sizeof(buf), stdin) != NULL)
   {
      val = strtol(buf, &p, 10);
      if(buf==p)
      {
        return 1;
      }
    if ( (val > INT_MAX || val < 0) || (errno != 0 && val == 0)) 
    {
           return 1;
            }
    if (*p != '\n' && *p != '\r' && *p != '\0')
    {
         return 1;
    }
  }
  else
  {
    return 1;
  }  

  *numero=(int)val;
  return 0;  
}
aeroxr1
  • 1,014
  • 1
  • 14
  • 36
  • 2
    `fgets` leaves the `\n` in the buffer. `strtol` leaves `p` at the next character after the last digit it successfully processed, which is `\n` if you entered some digits and pressed Enter. – M.M Jul 02 '14 at 09:51
  • possible duplicate of [Removing trailing newline character from fgets() input](http://stackoverflow.com/questions/2693776/removing-trailing-newline-character-from-fgets-input) – M.M Jul 02 '14 at 09:52
  • 1
    and because I used fgets to get the string if it's all ok therefore strtol leaves in p \n ! In the man strtol I read this : "If endptr is not NULL, strtol() stores the address of the first invalid character in *endptr. If there were no digits at all, strtol() stores the original value of nptr in *endptr (and returns 0). In particular, if *nptr is not '\0' but **endptr is '\0' on return, the entire string is valid." And I thought strtol left in p '\0' and not the next character after the last digit it successfully processed ! :D – aeroxr1 Jul 02 '14 at 10:10
  • The first invalid character is `\n`. `strtol` doesn't change any characters, it makes `p` point to a certain place in your input – M.M Jul 02 '14 at 10:15
  • 1) Need `errno = 0` right before `strtol()` call. Minor problems: 2) `if (errno == ERANGE)` is sufficient, no need to test the return value. 3) Not sure why your final code uses `val < 0` rather than `val < INT_MIN, just want non-negative numbers? 4) `(errno != 0 && val == 0)` likely wrong. Suggest `(errno)`. – chux - Reinstate Monica Jul 02 '14 at 13:36
  • More about `errno`. Per the C11 spec §7.22.1.4 8 "... If the correct value is outside the range of representable values, `LONG_MIN, LONG_MAX`, ... is returned ... and the value of the macro `ERANGE` is stored in `errno`." `errno` is not specified to change, if needed, to any other value other than `ERANGE` due to `strtol()`. When it does so, the `strtol()` return value is then 1 of 2 values: `LONG_MIN, LONG_MAX`. – chux - Reinstate Monica Jul 02 '14 at 13:43
  • Yes I use val>MAX_INT and val<0 because I want a only positive numbers :) Therefore I have to set errno=0 before use strtol() and you suggest me to control only (errno==ERANGE) without controlling (errno !=0 && val ==0) ? :) And last question : errno = 0 is for security because we don't really know what is there in errno ? :) – aeroxr1 Jul 02 '14 at 14:40

1 Answers1

4

Your code works perfectly. It has correctly identified that there are trailing characters after the number. As Matt says, gets returns a whole line, including the trailing line feed character (aside: why do people insist on posting the actual answer as a comment?).

Given that your string will almost always have this line feed, your simple fix is probably the right thing to do.

There are two exceptions though:

  1. end of file
  2. operating systems that use carriage returns at the end of line.

A more thorough solution would be this:

if (*p != '\n' && *p != '\r' && *p != '\0')
    {
        printf("error: unexpected trailing characters in input\n");
        temp=1;
    }
ams
  • 24,923
  • 4
  • 54
  • 75
  • Thanks :) With your suggest I correctly fix the problem :D And I understand that I have to study much better when the function leaves '\n' or leaves '\0' ! – aeroxr1 Jul 02 '14 at 10:27
  • When a file is operating in _text_ mode, the OS's line ending, be it `'\n'`, `'\r'`, `'\r\n'`, etc. are all translated to `'\n'`. Problems can occur if code is reading a text file with an alternate EOL per that OS. – chux - Reinstate Monica Jul 02 '14 at 13:23
  • True. Best to be sure though. – ams Jul 02 '14 at 13:25