【问题标题】:Difficulty using scanf for input难以使用 scanf 进行输入
【发布时间】:2015-12-04 08:36:48
【问题描述】:

有人可以帮我解决我的问题吗?我对%[^\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;
}

【问题讨论】:

  • 首先,如果你想成为标准的 C 兼容并且可移植,你不应该这样做 fflush(stdin),它在某些库中是一个扩展,但它在 C 规范中是明确未定义的行为。跨度>
  • 你所说的“错误输入”是什么意思?您能否展示一些示例输入以及您获得的实际(和预期)输出?请编辑您的问题以显示该信息。
  • 另外,如果strlen(name) &gt; 30 为真,那么您已经超出了数组的范围并且具有未定义的行为。数组name 最多可以包含 29 个字符的字符串,外加字符串终止符。
  • 最后,您可能想了解isalpha 函数。
  • 试图让scanf 在空白处不中断是受虐狂。使用fgets 读取所有行,然后将它们连接起来,或者使用getchar 读取所有内容。

标签: c scanf


【解决方案1】:

哦,顺便说一句,问题可能只是 scanf 调用将换行符留在缓冲区中,如果您循环并再次尝试,看到的第一个字符将是换行符,scanf 不应读取任何内容.

你应该做两件事:首先检查scanf返回什么返回,如果它读取一个字符串,它应该返回1。其次,您应该告诉scanf 通过在格式字符串中首先添加一个空格来丢弃任何可能的前导空格:" %[^\n]"

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


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

【讨论】:

  • 您还可以确保使用" %[^\n]%*c" 删除尾随换行符,这将读取并丢弃 newline 字符而不影响匹配计数 .
  • 非常感谢!我现在从来没有在 %[^\n] 之前谈论空间是啊哈哈
【解决方案2】:

这是你的程序修复:

#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() 函数的原因。很棘手,不是吗?

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

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

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

  • 当您设置了一个标志以指示错误条件时,在检查任何内容之前将其设置为“无错误”值会更容易,并且仅在出现错误的情况下才设置它到“错误”值。这将更容易阅读,因此也更容易理解。

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-02-20
    • 1970-01-01
    • 2020-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多