Thread: Strlen is return 1 on empty strings - fgets

  1. #1
    Registered User
    Join Date
    May 2017
    Posts
    61

    Strlen is return 1 on empty strings - fgets

    Code:
    int size = 0;
    
        do {
            puts("Insert the ID?");
            fgets(buffer.idarea, MAX, stdin);
            strtok(buffer.idarea, "\n"); // Consumir o \n 
            printf("size of string %d\n", size = strlen(buffer.idarea));
        } while (verifica_area_duplicadas(vector, *total, buffer.idarea) == 0);
    My problem is strlen returns 1 on empty strings

    If i write something like "a" it returns 1 too.

    My final goal is to put one more condition on while so the user canīt input empty strings.

  2. #2
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    strtok will not remove the newline (or any of the given separators) if there aren't any non-separator characters in the string. However, if there are no non-separator characters in the string, strtok will return NULL, so you can use that to tell if the line is empty. You may as well include other whitespace characters in the separator list, and you can even use the returned pointer to skip any initial whitespace.
    Code:
    #include <stdio.h>
    #include <string.h>
    
    int main() {
        char str[100];
    
        while (1) {
            puts("Enter the ID:");
            if (fgets(str, sizeof str, stdin) == NULL)
                break;
            char *p = NULL;
            if ((p = strtok(str, " \t\n")) == NULL)
                continue;
            printf("size of string %d\n", (int)strlen(p));
        }
    
        return 0;
    }
    Try entering some spaces, 12345, and some more spaces.


    Alternatively, you could use strcspn like so:
    Code:
    #include <stdio.h>
    #include <string.h>
    
    int main() {
        char str[100];
    
        while (1) {
            puts("Enter the ID:");
            if (fgets(str, sizeof str, stdin) == NULL)
                break;
            str[strcspn(str, "\n")] = '\0';
            printf("size of string %d\n", (int)strlen(str));
        }
    
        return 0;
    }
    Or, if you're really interested in the string length, then you may as well do it in the straightforward way:
    Code:
    #include <stdio.h>
    #include <string.h>
    
    int main() {
        char str[100];
    
        while (1) {
            puts("Enter the ID:");
            if (fgets(str, sizeof str, stdin) == NULL)
                break;
    
            size_t len = strlen(str);
            if (str[len-1] == '\n')
                str[--len] = '\0';
    
            printf("size of string %d\n", (int)len);
        }
    
        return 0;
    }
    If you aren't really interested in the string length, the the strtok method is excellent since it allows you to skip initial and trailing space, and with a little extension could be used to detect extraneous (presumably erroneous) non-space characters after the first word.
    Code:
    #include <stdio.h>
    #include <string.h>
    
    int main() {
        char str[100];
    
        while (1) {
            puts("Enter the ID:");
            if (fgets(str, sizeof str, stdin) == NULL)
                break;
            char *p = NULL;
            if ((p = strtok(str, " \t\n")) == NULL)
                continue;
            if (strtok(NULL, " \t\n") != NULL) {
                printf("Extraneous characters in input!\n");
                continue;
            }
            printf("size of string %d\n", (int)strlen(p));
        }
    
        return 0;
    }
    This considers "one two" an error, but " one " is okay (and is interpreted as "one"). (I meant to add a few spaces before and after "one", but the forum reduces them to one space.)
    Last edited by algorism; 05-11-2017 at 06:27 PM.

  3. #3
    Registered User rstanley's Avatar
    Join Date
    Jun 2014
    Location
    New York, NY
    Posts
    733
    Code:
    puts("Insert the ID?");
    fgets(buffer.idarea, MAX, stdin);
    If at the prompt, the user justs presses the [Enter] key, a newline will be input, and the strlen(buffer.idarea) will be one, not zero!

    You need to remove the newline right after entering the string before testing the length of the string. Even if the user entered valid data, the newline should be removed!

    Please read the man page for fgets().

  4. #4
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    Quote Originally Posted by rstanley View Post
    If at the prompt, the user justs presses the [Enter] key, a newline will be input, and the strlen(buffer.idarea) will be one, not zero!
    I think he knows that, which is why he's using strtok to remove the newline. He just didn't know that strtok(str, "\n") won't remove the newline if that's all that's in str.

  5. #5
    Registered User
    Join Date
    May 2017
    Posts
    61
    Hi,

    Still missing something, you last piece of code works great but then i cannot use it on “do while” condition for some reason.

    I wrote the following code.

    Code:
    do {
            puts("Qual e o ID da area?");
            if (fgets(buffer.idarea, sizeof (buffer.idarea), stdin) == NULL)
                break;
            buffer.idarea[strlen(buffer.idarea) - 1] = '\0'; // Limpar o /n                
        } while ( (int) strlen(buffer.idarea) == 0 || verifica_area_duplicadas(vector, *total, buffer.idarea) == 0);
    But not all tests are working
    Test1: “empty” - Gives me 0 – GOOD
    Test2: “test” - Gives me 4 - GOOD
    Test3: “test more” - Gives me 4 – NOT GOOD I just want the first word
    Test4: “ test” - Gives me 10 - NOT GOOD
    Test4: “ test ” - Gives me 14 - NOT GOOD
    Test4: test ” - Gives me 8 - NOT GOOD

    I just want to remove all spaces from my string on the begin and the end and allow only one word then use de size to campare on the “do while cycle”.



    Quote Originally Posted by algorism View Post
    strtok will not remove the newline (or any of the given separators) if there aren't any non-separator characters in the string. However, if there are no non-separator characters in the string, strtok will return NULL, so you can use that to tell if the line is empty. You may as well include other whitespace characters in the separator list, and you can even use the returned pointer to skip any initial whitespace.
    Code:
    #include <stdio.h>
    #include <string.h>
    
    int main() {
        char str[100];
    
        while (1) {
            puts("Enter the ID:");
            if (fgets(str, sizeof str, stdin) == NULL)
                break;
            char *p = NULL;
            if ((p = strtok(str, " \t\n")) == NULL)
                continue;
            printf("size of string %d\n", (int)strlen(p));
        }
    
        return 0;
    }
    Try entering some spaces, 12345, and some more spaces.


    Alternatively, you could use strcspn like so:
    Code:
    #include <stdio.h>
    #include <string.h>
    
    int main() {
        char str[100];
    
        while (1) {
            puts("Enter the ID:");
            if (fgets(str, sizeof str, stdin) == NULL)
                break;
            str[strcspn(str, "\n")] = '\0';
            printf("size of string %d\n", (int)strlen(str));
        }
    
        return 0;
    }
    Or, if you're really interested in the string length, then you may as well do it in the straightforward way:
    Code:
    #include <stdio.h>
    #include <string.h>
    
    int main() {
        char str[100];
    
        while (1) {
            puts("Enter the ID:");
            if (fgets(str, sizeof str, stdin) == NULL)
                break;
    
            size_t len = strlen(str);
            if (str[len-1] == '\n')
                str[--len] = '\0';
    
            printf("size of string %d\n", (int)len);
        }
    
        return 0;
    }
    If you aren't really interested in the string length, the the strtok method is excellent since it allows you to skip initial and trailing space, and with a little extension could be used to detect extraneous (presumably erroneous) non-space characters after the first word.
    Code:
    #include <stdio.h>
    #include <string.h>
    
    int main() {
        char str[100];
    
        while (1) {
            puts("Enter the ID:");
            if (fgets(str, sizeof str, stdin) == NULL)
                break;
            char *p = NULL;
            if ((p = strtok(str, " \t\n")) == NULL)
                continue;
            if (strtok(NULL, " \t\n") != NULL) {
                printf("Extraneous characters in input!\n");
                continue;
            }
            printf("size of string %d\n", (int)strlen(p));
        }
    
        return 0;
    }
    This considers "one two" an error, but " one " is okay (and is interpreted as "one"). (I meant to add a few spaces before and after "one", but the forum reduces them to one space.)

  6. #6
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    38,127
    fgets() doesn't do 'words'.
    It reads a line of input until
    - the buffer is full (anything that doesn't fit is left on the input stream for the next call)
    - a \n character is read (the buffer is returned)
    - EOF is detected.

    Code:
    char buff[BUFSIZ];
    while ( fgets(buff,sizeof(buff),stdin) != NULL ) {
      char str[100];
      if ( sscanf(buff,"%99s",str) == 1 ) {
        // str contains the first 'word' of input, with leading spaces
        // and trailing junk discarded.
      }
    }
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  7. #7
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    You should probably use something based on the last piece of code that I gave (which you seem to say you are using, but you aren't).
    Code:
        do {
            char line[1000], *pline = NULL;
            puts("Qual e o ID da area?");
            if (fgets(line, sizeof line, stdin) == NULL)
                break; // whether this is correct depends on the rest of your code
    
            if ((pline = strtok(str, " \t\n")) == NULL)
                continue;  // empty line
    
            if (strtok(NULL, " \t\n") != NULL) {
                printf("Extraneous characters in input!\n");
                continue;
            }
    
            if (strlen(pline) + 1 > sizeof buffer.idarea) {
                printf("Entry is too long!\n");
                continue;
            }
    
            if (!verifica_area_duplicadas(vector, *total, pline)) {
                printf("Duplicate entry!\n");
                continue;
            }
    
            strcpy(buffer.idarea, pline);    
        } while (0); // the loop is just for the continues

  8. #8
    Registered User
    Join Date
    May 2017
    Posts
    61
    I found this solution.


    Code:
        int controlinput = 0;   
    
    
        do {
            while (controlinput == 0) {
                puts("Insert ID here?");
                if (fgets(buffer.idarea, MAX, stdin) == NULL)
                    break;
    
    
                if (sscanf(buffer.idarea, " %s", buffer.idarea) != 1) {
                    printf("Invalid String\n");
                } else {
                    // String is valid
                    controlinput = 1;
                }
            }
            // buffer.idarea[strlen(buffer.idarea) - 1] = '\0'; // Clear /n       
    
    
        } while ((int) strlen(buffer.idarea) == 0 || verifica_area_duplicadas(vector, *total, buffer.idarea) == 0);
    What you think about my approach ?


    Your solution a little to advanced for me yet.

    Quote Originally Posted by algorism View Post
    You should probably use something based on the last piece of code that I gave (which you seem to say you are using, but you aren't).
    Code:
        do {
            char line[1000], *pline = NULL;
            puts("Qual e o ID da area?");
            if (fgets(line, sizeof line, stdin) == NULL)
                break; // whether this is correct depends on the rest of your code
    
            if ((pline = strtok(str, " \t\n")) == NULL)
                continue;  // empty line
    
            if (strtok(NULL, " \t\n") != NULL) {
                printf("Extraneous characters in input!\n");
                continue;
            }
    
            if (strlen(pline) + 1 > sizeof buffer.idarea) {
                printf("Entry is too long!\n");
                continue;
            }
    
            if (!verifica_area_duplicadas(vector, *total, pline)) {
                printf("Duplicate entry!\n");
                continue;
            }
    
            strcpy(buffer.idarea, pline);    
        } while (0); // the loop is just for the continues

  9. #9
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    38,127
    > What you think about my approach ?
    Well using the same buffer as the source and destination in sscanf is undefined behaviour.

    You really need to get away from reading input directly into the memory where you want the result stored.

    It's much cleaner to read into a temp variable, check for input errors, do some kind of validation on the data, and then copy to where you want the data stored.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  10. #10
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    Using the same string as both the input and output of sscanf is insane! You can't do that. Use a different string for the input from fgets.

    You don't need the controlinput flag. Just use an infinite while loop (while (1)) and replace controlinput = 1 with a break.

    Your strlen test is poinless now since the string can't be empty.
    Code:
    do {
        char line[1000];
        while (1) {
            puts("Insert ID here?");
            if (fgets(line, sizeof line, stdin) == NULL)
                break; // whether this is right still depends on the rest of your code
    
            // Replace the 255 below with the size of buffer.idarea - 1.
            if (sscanf(line, " %255s", buffer.idarea) != 1)
                printf("Invalid String\n");
            else
                break;
        }
    } while (verifica_area_duplicadas(vector, *total, buffer.idarea) == 0);
    This still allows input like "one two three", storing just "one" in idarea and ignoring the rest.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. fgets() and strlen() confusion
    By rwagstaff in forum C Programming
    Replies: 11
    Last Post: 09-25-2012, 06:17 PM
  2. strstr doesn't return FALSE for empty string
    By doia in forum C Programming
    Replies: 3
    Last Post: 07-12-2010, 01:02 PM
  3. empty strings
    By KIBO in forum C++ Programming
    Replies: 10
    Last Post: 01-10-2008, 09:45 PM
  4. strlen doesn't return a const
    By johnp in forum C Programming
    Replies: 10
    Last Post: 10-13-2005, 04:20 PM
  5. passing strings and using strlen
    By Unregistered in forum C++ Programming
    Replies: 1
    Last Post: 09-15-2001, 10:41 PM

Tags for this Thread