Thread: fgets() and strlen() confusion

  1. #1
    Registered User
    Join Date
    Sep 2012
    Posts
    15

    fgets() and strlen() confusion

    Ok I am puzzled once again. I just don't understand how C and these functions work. I am trying to find the length of a string that is input. Two problems show up: 1) It appears fgets() is storing a newline unless I input all 8 characters and 2) strlen() is counting 1 more char than there are except when I reach the max of the string and then it counts the correct number of characters. How do you prevent fgets() from storing newline? To me this should be so trivial yet it is confusing me. Any help is appreciated. Thank you.

    Code:
        char str[8];
        printf("Enter text: ");
        fgets(str, sizeof(str), stdin);
        printf("\nThe string %s has a length of %u.\n", str, strlen(str));
    Output 1:
    Enter text: CAFE


    The string CAFE
    has a length of 5.
    Press any key to continue . . .

    Output 2:
    Enter text: 12345678


    The string 1234567 has a length of 7.
    Press any key to continue . . .

  2. #2
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    945
    fgets does store a newline character in the string, as you correctly noted. strlen counts all the characters in a string, including the newline, so "CAFE\n" actually is 5 characters long.

    If you want to remove the newline character, check that the last character in the string is a newline and then overwrite it with a null terminator ('\0').

  3. #3
    Registered User
    Join Date
    Sep 2012
    Posts
    15
    Thanks got it. And just to confirm fgets() is better than scanf() right??

  4. #4
    Been here, done that.
    Join Date
    May 2003
    Posts
    1,164
    Yes. Much better -- because it's safer.
    Definition: Politics -- Latin, from
    poly meaning many and
    tics meaning blood sucking parasites
    -- Tom Smothers

  5. #5
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    Quote Originally Posted by WaltP View Post
    Yes. Much better -- because it's safer.
    scanf() can be made safe by using the conversion specifiers in your code -
    Code:
    //let's say...
    char s[20];
    memset(s, '\0', sizeof(s));
    
    scanf("%10s",s); //Read max 10 digits
    printf("%s",s);
    Fact - Beethoven wrote his first symphony in C

  6. #6
    Registered User claudiu's Avatar
    Join Date
    Feb 2010
    Location
    London, United Kingdom
    Posts
    2,094
    Quote Originally Posted by Click_here View Post
    scanf() can be made safe by using the conversion specifiers in your code -
    Code:
    //let's say...
    char s[20];
    memset(s, '\0', sizeof(s));
    
    scanf("%10s",s); //Read max 10 digits
    printf("%s",s);
    Not necessarily always safe, but certainly safer.

    What about this case:

    Code:
    char *str;
    size_t len;
    
    /*Read len from the user whichever way you like */
    
    str = malloc(len);
    
    /* later on */
    
    scanf("%10s",str);
    1. Get rid of gets(). Never ever ever use it again. Replace it with fgets() and use that instead.
    2. Get rid of void main and replace it with int main(void) and return 0 at the end of the function.
    3. Get rid of conio.h and other antiquated DOS crap headers.
    4. Don't cast the return value of malloc, even if you always always always make sure that stdlib.h is included.

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by claudiu
    Not necessarily always safe, but certainly safer.

    What about this case:
    That is a bug that is not due to the use of scanf with the field width correctly specified, hence the use of scanf in your example is indeed safe, once that bug has been fixed. In fact, if you want to blame scanf for the bug rather than the programmer, then indeed there is nothing that can be substituted that will be safe. Absolutely nothing, ever, other than the programmer.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    Registered User claudiu's Avatar
    Join Date
    Feb 2010
    Location
    London, United Kingdom
    Posts
    2,094
    I don't want to blame anyone for anything.
    I was just offering some food for thought that you need to be aware of what you are doing when using scanf. Of course there is a bug in there, that was the whole point, to show that you can trip yourself with scanf even if you specify the size of the string you are reading in. The question originally was related to the ancient fgets() vs scanf debate and the fact that fgets is so pedantic and requires a size parameter makes it safer, because it makes you, the programmer, immediately aware of potential overflow problems.
    1. Get rid of gets(). Never ever ever use it again. Replace it with fgets() and use that instead.
    2. Get rid of void main and replace it with int main(void) and return 0 at the end of the function.
    3. Get rid of conio.h and other antiquated DOS crap headers.
    4. Don't cast the return value of malloc, even if you always always always make sure that stdlib.h is included.

  9. #9
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by claudiu
    I was just offering some food for thought that you need to be aware of what you are doing when using scanf.
    Certainly, but you need to be aware of what you are doing when you are programming. The use of fgets is no real improvement if you are careless, e.g.,
    Code:
    char *str;
    size_t len;
    
    /*Read len from the user whichever way you like */
    
    str = malloc(len);
    
    /* later on */
    
    fgets(str, len, stdin);
    The above code is vulnerable to buffer overflow because len was doubled between the calls to malloc and fgets.

    Likewise, we can "fix" the "problem" with scanf in your (counter-)example by making it such that the field width is not hard coded into the format string, but then the resulting code will be equally vulnerable to buffer overflow as in my scenario.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  10. #10
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    I think that saying along the lines of the programmer might introduce a bug with the scanf statement, so it should be avoided is not right.

    I can introduce the same bug using a "for" or a "while" statement, but you wouldn't even think of saying, "avoid for and while loops".
    Fact - Beethoven wrote his first symphony in C

  11. #11
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    You can use scanf() super safely but it can be a pain in the ass.
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        char *data = NULL;
        char format[16];
        int length = 0;
        int convert = 0;
        puts("Please enter a string length.");
        convert = scanf("%i", &length);
        if (convert == 1 && length > 1)
        {
            data = malloc(1 + length);
            sprintf(format, "%%%ds", length);
        }
        else
        {
            fputs("Bad string length entered.\n",stderr);
            return 1;
        }
    
        printf("OK, enter your string (length = %d).\n", length);
        convert = scanf(format, data);
        if (convert == 1)
        {
            printf("Good job.\n\"%s\"\n", data);
        }
        else
        {
            fputs("There was a problem.\n",stderr);
        }
        free(data);
        data = NULL;
    
        return 0;
    }
    
    /*
    Please enter a string length.
    20
    OK, enter your string (length = 20).
    Low-calorie.beer
    Good job.
    "Low-calorie.beer"
    */
    Basically you need to do that. I guess it could look worse, and I didn't check malloc(). Oh well. There's annoying things about scanf that won't be fixed, like not reading strings longer than one word.

    [edit] It's not that scanf is a bad function that works poorly, but user input is frequently not formatted at all. Use scanf to read formatted files or something. It can be a lot of work to shoehorn this one way you know to get input into the program securely.[/edit]
    Last edited by whiteflags; 09-25-2012 at 06:15 PM. Reason: So it turns out laserlight mentioned this...

  12. #12
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    945
    Quote Originally Posted by whiteflags View Post
    Basically you need to do that. I guess it could look worse, and I didn't check malloc(). Oh well. There's annoying things about scanf that won't be fixed, like not reading strings longer than one word.
    Yes, it is annoying to create a format string for scanf, but scanf does have a way to read arbitrary strings, including whitespace, using the '[' format specifier. A common format is "%[\n]" (or something like "%20[\n]" with a maximum length).

    I do prefer using fgets, though, since users in general expect the program to read whole lines at a time. sscanf can then be used on each input line string if needed.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Strlen
    By _arjun in forum C Programming
    Replies: 9
    Last Post: 06-10-2012, 11:20 AM
  2. strlen
    By salvadoravi in forum C Programming
    Replies: 15
    Last Post: 01-11-2008, 05:43 PM
  3. strlen()
    By exoeight in forum C Programming
    Replies: 9
    Last Post: 04-01-2005, 10:18 AM
  4. Strlen(...)
    By Korhedron in forum C++ Programming
    Replies: 6
    Last Post: 06-10-2003, 03:02 PM
  5. Why O why, strlen?
    By Sebastiani in forum C Programming
    Replies: 11
    Last Post: 08-24-2001, 01:41 PM