简体   繁体   中英

Difficulty using scanf for input

Can someone help me to solve my problem? I have a problem with %[^\\n] . When I try to enter a false input the program loop the warning that I wrote, but if I use %s and I enter my string the next statement is not working properly.

#pragma warning (disable:4996)
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int main(){
    char name[30];
    char number[12];
    int flag, flag1, flag2, flag3;
    int i;


    printf("Add New Contact\n");
    do {

    printf("input name [1..30 char]: ");
    scanf("%[^\n]", name); fflush(stdin); 

        if ((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z')) {
            flag = 1;
        }
        else {
            flag = 0;
            printf("First letter of name should be an alphabet (A-Z or a-z)\n");

        }

        if (strlen(name) > 30) {
            flag1 = 0;
            printf("Length of name should be between 1 and 30 characters\n");
        }
        else {

            flag1 = 1;
        }



    } while (flag == 0 || flag1 == 0);

    do {
        printf("Input phone number[6..12 digits]: ");
        scanf("%s", number); fflush(stdin);

        for (i = 0; i < strlen(number); i++) {
            if (number[i] >= '0' && number[i] <= '9') {
                flag2 = 1;
            }
            else {
                flag2 = 0;
            }
        }
        if (flag2 == 0) {
            printf("Phone numbers should only contain digits (0-9)\n");
        }
        if (strlen(number) >= 6 && strlen(number) <= 12) {
            flag3 = 1;
        }
        else {
            flag3 = 0; 
            printf("Length of phone numbers should be between 6 and 12 digits\n");
        }



    } while (flag2 == 0 || flag3 == 0);

    printf("\n");
    printf("New contact successfully added!\n");
    printf("Press Enter to continue...");

    getchar();
    getchar();
    return 0;
}

Oh by the way, the problem might simply be that the scanf call leaves the newline in the buffer, and if you loop and try again the first character seen will be the newline and scanf should not read any thing.

There are two things you should do: First check what scanf returns , it should return 1 if it read a string. Secondly you should tell scanf to discard any possible leading white-space by adding a space first in the format string: " %[^\\n]" .

Most scanf formats automatically skips leading white-space, but not when using the "%[" or "%c" formats.


Also, to not worry about writing out of bounds of the array, you should add a length modifier to make sure that scanf doesn't read more input than it can write: " %29[^\\n]" . If the length of the string is 29 after this, then you should probably read until you reach the end of the line, character by character.

Here is your program fixed:

#include <stdio.h>
#include <ctype.h>
#include <string.h>

// In case you need this -- not needed for this case
void discard_input()
{
    char c;

    while( ( c = getchar() ) != '\n' && c != EOF );
}

void remove_trailing_newline(char * s)
{
    char * ch = s + strlen( s ) - 1;

    while( ch != s ) {
        if ( *ch == '\n' ) {
            *ch = 0;
            break;
        }

        --ch;
    }

    return;
}

int main(){
    char name[30];
    char number[12];
    int flag, flag1, flag2, flag3;
    int i;


    printf("Add New Contact\n");
    do {

    printf("\nInput name [1..30 char]: ");
    fgets( name, 30, stdin );
    remove_trailing_newline( name );
    flag1 = flag = 1;

        if ( !isalpha( name[ 0 ] ) ) {
            flag = 0;
            printf("First letter of name should be an alphabet (A-Z or a-z), found: %s\n", name );

        }

        // impossible
        if (strlen(name) > 30) {
            flag1 = 0;
            printf("Length of name should be between 1 and 30 characters\n");
        }

    } while (flag == 0 || flag1 == 0);

    do {
        printf("\nInput phone number[6..12 digits]: ");
        fgets( number, 12, stdin );
        remove_trailing_newline( number );
        flag2 = flag3 = 1;
        int len_phone = strlen( number );

        for (i = 0; i < strlen(number); i++) {
            if ( !isdigit( number[ i ] ) ) {
                flag2 = 0;
            }
        }

        if (flag2 == 0) {
            printf("Phone numbers should only contain digits (0-9), found:'%s'\n", number);
        }

        if ( len_phone < 6 || len_phone > 12) {
            flag3 = 0; 
            printf("Length of phone numbers should be between 6 and 12 digits, found: %d\n", len_phone );
        }

    } while (flag2 == 0 || flag3 == 0);

    printf("\n");
    printf( "Name: '%s'\n", name );
    printf( "Phone: '%s'\n", number );
    printf("New contact successfully added!\n");
    printf("Press Enter to continue...");
    getchar();
    return 0;
}

You can find the program here .

The fixings are more or less interesting, I enumerate they here:

  • At first, I thought that the problem was that the trailing new line was being left in the input buffer. fflush(stdin) is actually undefined behaviour in C, since the fflush() function is there for output streams. Anyway, I included the code in question 12.26b of the comp.lang.c FAQ , since I think it is interesing to have it as reference. Then, I decided to change scanf() with fgets() . This is due to the scanf() taking spaces as delimiters, so you wouldn't be able to write a complete name, ie, name and surname. Remember that gets() is not an option, since it writes the input past the limit of the buffer. Actually, fgets() solves this by letting us define a limit of chars to read. The problem is that fgets() also includes the '\\n' in the buffer, so, that's why I included the remove_trailing_newline() function. Tricky, isn't it?

  • You added a condition to check whether the name input had more than thirty chars. Actually, this is impossible to check in your program. First of all, fgets() will read 29 chars + the final char mark (0). Secondly, if you were actually allowing to input more than 30 chars, then the input would be written past the size of the buffer, which is undefined behaviour (crashes in most cases). You would have to use something more complex, like std::string in C++, and then check its length. Or maybe use a third party expandable string for C. Or roll out your own expandable string ...

  • You can decide whether there is an alphabetic char or a digit by using isalpha(c) and isdigit(c) functions.

  • When you are going to use a value many times, such as strlen(name) , then you should precompute it and store it in a local variable. Though a good compiler (its optimizer) will detect this situation and solve it for you, you never know which compiler is going to compile your code, and how advanced it is. Also, there is nothing wrong making things easier for the optimizer.

  • When you have a situation in which you set a flag for signaling an error condition, it is easier to set it to the "no error" value before checking anything, and solely in case of an error, set it to the "error" value. This will be easier to read, and therefore, to understand.

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM