难以使用 scanf 进行输入

Difficulty using scanf for input

有人可以帮我解决我的问题吗?我对 %[^\n] 有疑问。当我尝试输入错误输入时,程序循环我写的警告,但如果我使用 %s 并输入我的字符串,则下一条语句无法正常工作。

#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;
}

哦,顺便说一句,问题可能只是 scanf 调用在缓冲区中留下了换行符,如果您循环并重试,看到的第一个字符将是换行符和 scanf 不应该读任何东西。

有两件事你应该做:首先检查scanfreturns,它应该return1如果它读取一个字符串。其次,您应该通过在格式字符串中首先添加 space 来告诉 scanf 丢弃任何可能的前导白色-space:" %[^\n]".

大多数 scanf 格式会自动跳过前导白色-space,但在使用 "%[""%c" 格式时不会。


此外,为了不担心写出数组的边界,您应该添加一个长度修饰符以确保 scanf 读取的输入不会超过它可以写入的输入:" %29[^\n]" .如果在此之后字符串的长度是 29,那么您可能应该逐个字符地阅读直到到达行尾。

这是你的程序修复:

#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;
}

您可以找到程序 here

修复或多或少很有趣,我在这里列举它们:

  • 起初,我认为问题是尾随的新行被留在输入缓冲区中。 fflush(stdin) 实际上是 C 中的未定义行为,因为 fflush() 函数用于输出流。无论如何,我将代码包含在 question 12.26b of the comp.lang.c FAQ 中,因为我认为将其作为参考很有趣。然后,我决定把scanf()改成fgets()。这是由于 scanf() 以空格作为分隔符,因此您无法写出完整的名称,即姓名和姓氏。请记住 gets() 不是一个选项,因为它写入的输入超过了缓冲区的限制。实际上,fgets() 通过让我们定义要读取的字符限制来解决这个问题。问题是 fgets() 还在缓冲区中包含 '\n',所以,这就是我包含 remove_trailing_newline() 函数的原因。很棘手,不是吗?

  • 您添加了一个条件来检查输入的名称是否超过三十个字符。实际上,这是不可能签入您的程序的。首先,fgets() 将读取 29 个字符 + 最终字符标记 (0)。其次,如果您实际上允许输入超过 30 个字符,那么输入将超过缓冲区的大小,这是未定义的行为(在大多数情况下会崩溃)。您将不得不使用更复杂的东西,例如 C++ 中的 std::string,然后检查其长度。或者可以为 C 使用第三方可扩展 string。或者推出您自己的 expandable string...

  • 您可以使用isalpha(c)isdigit(c)函数来决定是字母字符还是数字。

  • 当你要多次使用一个值时,比如strlen(name),那么你应该预先计算它并将它存储在一个局部变量中。虽然一个好的编译器(它的优化器)会检测到这种情况并为您解决,但您永远不知道哪个编译器将编译您的代码,以及它有多先进。此外,让优化器更容易做事也没有错。

  • 当您遇到设置标志以指示错误情况的情况时,在检查任何内容之前将其设置为 "no error" 值更容易,并且仅在以下情况下一个错误,将其设置为 "error" 值。这将更容易阅读,因此也更容易理解。

希望这对您有所帮助。