scanf(“%[^ \ n] d”,x)或scanf(“%[0-9] d”,x)将1变为49,将2变为50

问题描述 投票:4回答:1

Problem:

我遇到scanf()的问题。我从阅读论坛等知道scanf()在C语言中很成问题,但我还在学习基础知识,所以我不知道所有的细节。

The code snippet that I'd like to solve.

#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

struct Biodata {
    char name[21],
    address[65],
    date[11],
    phone[17];
};

int main() {
    struct Biodata bio[10];
    int input_max = 0,
    index_struct = 0;
    while (printf("Input the amount of data! ") && scanf("%[0-9]*d", &input_max) < 1) {
        printf("Error! Please, try again!\n\n");
       fflush(stdin);
    }
    for (index_struct = 1; index_struct <= input_max; index_struct++) {
        printf("Your input data count is %d.\n", input_max);
        printf("Data %d.\n", index_struct);
        fflush(stdin);
        while (printf("Name\t: ") && scanf("%[A-Z a-z]s", &bio[index_struct].name) < 1) {
            printf("Error! Please, try again!\n\n");
            fflush(stdin);
        }
        fflush(stdin);
        while (printf("Address\t: ") && scanf("%[^\n]s", &bio[index_struct].address) < 1) {
            printf("Error! Please, try again!\n\n");
            fflush(stdin);
        }
        fflush(stdin);
        while (printf("Birthdate\t: (YYYY/MM/DD)\n") && scanf("%[^\n A-Z a-z]s", &bio[index_struct].date) < 1) {
            printf("Error! Please, try again!\n\n");
            fflush(stdin);
        }
        fflush(stdin);
        while (printf("Phone Number\t: ") && scanf("%[^\n A-Z a-z]s", &bio[index_struct].phone) < 1) {
            printf("Error! Please, try again!\n\n");
            fflush(stdin);
        }
        fflush(stdin);
        printf("\n");
    }
    printf("Input the index number you'd like the data to be pulled from! ");
    scanf("%d", &index_struct);
    printf("%-10s\n%-64s\n%-10s\n%-16s",
           bio[index_struct].name, bio[index_struct].address,
           bio[index_struct].date, bio[index_struct].phone);
    return 0;
}

当输入是空白时,我正在尝试使每个输入能够输出错误。 [^\n][A-Z][0-9]的扫描集通常可以帮助我处理更简单的情况。但是,在这一个中,当我在input_max中输入任意数字作为输入时

while (printf("Input the amount of data! ") && scanf("%[0-9]*d", &input_max) < 1) {
    printf("Error! Please, try again!\n\n");
    fflush(stdin);
} 

input_max给出的数字与给出的数字不同。这里发生了什么?我该怎么做才能绕过它?

我也不完全理解代码片段如何作为错误输出工作,因为我发现在某处在线。

编辑:正如@JonathanLeffler所建议的那样,scanf()将我的输入作为ASCII,ISO 8859-x或Unicode或所有这些代码点的代码点。但是,当我删除扫描集时,输入保持不变,将其转换为scanf(%d, &input_max)。但是,我确实需要扫描集,所以我可以输入一个空格,并在我输入scanf()中的空格时设置弹出的错误消息。

c scanf
1个回答
4
投票

你误认为[%s%d的修饰符 - 例如。 %3d-它不是。 %[本身是一个转换说明符,其作用类似于%s

因此,正如@ user3629249的评论中指出的那样,s说明符末尾的d%[(例如在%[^\n A-Z a-z]s中)是无关紧要的。 %[的空间也很重要。所以%[A-z a-z]%[A-Za-z]不同

让我们看一下打开格式警告时编译时遇到的问题。 (-Wformat如果你使用gcc或clang)你会得到类似的东西:

foo.c:19:68: warning: format specifies type 'char *' but the argument has type 'int *' [-Wformat]
  while (printf("Input the amount of data! ") && scanf("%[0-9]*d", &input_max)<1) {
                                                        ~~~~~      ^~~~~~~~~~
                                                        %d
foo.c:29:55: warning: format specifies type 'char *' but the argument has type 'char (*)[21]' [-Wformat]
    while (printf("Name\t: ") && scanf("%[A-Z a-z]s", &bio[index_struct].name)<1) {
                                        ~~~~~~~~~     ^~~~~~~~~~~~~~~~~~~~~~~
foo.c:34:54: warning: format specifies type 'char *' but the argument has type 'char (*)[65]' [-Wformat]
    while (printf("Address\t: ") && scanf("%[^\n]s", &bio[index_struct].address)<1) {
                                           ~~~~      ^~~~~~~~~~~~~~~~~~~~~~~~~~
foo.c:39:78: warning: format specifies type 'char *' but the argument has type 'char (*)[11]' [-Wformat]
    while (printf("Birthdate\t: (YYYY/MM/DD)\n") && scanf("%[^\n A-Z a-z]s", &bio[index_struct].date)<1) {
                                                           ~~~~~~~~~~~~~     ^~~~~~~~~~~~~~~~~~~~~~~
foo.c:44:67: warning: format specifies type 'char *' but the argument has type 'char (*)[17]' [-Wformat]
    while (printf("Phone Number\t: ") && scanf("%[^\n A-Z a-z]s", &bio[index_struct].phone)<1) {
                                                ~~~~~~~~~~~~~     ^~~~~~~~~~~~~~~~~~~~~~~~

您的代码还有其他问题:

  1. 你将你的Biodata从1索引到10(看看index_struct的循环)然而它被声明为一个大小为10的数组Biodata bio[10];

在C数组中基于0,所以它们从0size-1,你的for循环会遇到分段错误,因为bio[10]将是未定义的。

  1. 你在for循环中要求input_max,但是你需要for for循环。
  2. 如果input_max大于bio数组的声明大小,会发生什么?

其他一些好事要考虑:

  1. printf是一个用于报告错误的错误函数,错误应该转到stderr,而不是stdout,因此最好使用fprintf并指定stderr。
  2. 因为您有兴趣确保正确解析输入,为什么不创建自己的解析器而不是使用scanf?
  3. 你正在强制重新提示错误,让我们把它分解成自己的功能。

Let's do it together.

Note on the style of C and naming conventions I use below

我的C风格与你的有点不同,我有我的理由:-),因为这只是意见,让我们跟我的一起去吧。

该结构对我们想要的内容有一些评论

struct biodata {
    char name[21];        /* format: FirstName LastName */
    char address[65];     /* format: Free-form upto 65 chars */
    char birthday[11];    /* format: YYYY/MM/DD */
    char phone[17];       /* format: up to digits or a spaces */
};

一些匹配者

下面是一组匹配函数,它们接收输入行并告诉我们该行是否完全符合我们的预期。如果是,则返回true,否则返回false。你需要#include <stdbool.h>这个。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>

struct biodata {
        char name[21];        /* format: FirstName LastName */
        char address[65];     /* format: Free-form upto 65 chars */
        char birthday[11];    /* format: YYYY/MM/DD */
        char phone[17];       /* format: up to digits or a spaces */
};


bool match_name(const char *line)
{
    char tmp[128];
        return line!=NULL
        && sscanf(line, "%128[A-Za-z]%128[ ]%128[A-Za-z]", tmp, tmp, tmp) == 3
        && strlen(tmp) < 21;
}

bool match_address(const char *line)
{
    return line != NULL
        && strlen(line) > 5
        && strlen(line) < 65; /* no format check here, maybe later */
}

bool match_telephone(const char *line) 
{
    char tmp[128];
    return line  /* notice the alternate form of checking line!=NULL */
        && sscanf(line, "%128[0-9 ]", tmp)==1
        && strlen(tmp) < 17;
}

/* here we'll use strptime to see if our line is a valid date */
bool match_birthday(const char *line) 
{
     struct tm tm; /* time structure */
     if(!line)
          return false;

     return strptime(line, "%Y/%m/%d", &tm) != NULL;
}

char * ask(const char *prompt, char *line, size_t maxlen) 
{
    printf("%-30s:", prompt);
    fflush(stdout);
    fgets(line, maxlen, stdin);
    return line; /* we return the pointer for ease of use */
}

/* a test function */
void test_matchers() {

    char line[256];
    /* remember ask returns our line so we are able to pass it to our matchers */
    while(!match_name(ask("Name (first last)", line, sizeof(line))))
        ;
    while(!match_address(ask("Address (at least 5 chars)", line, sizeof(line))))
        ;
    while(!match_birthday(ask("Birthday (YYYY/MM/DD)", line, sizeof(line))))
        ;
    while(!match_telephone(ask("Telephone (max 16 digits)", line, sizeof(line))))
        ;
}


int main() 
{
     test_matchers();
     return 0;
}

测试一下。

$ ./bar

Name (first last)             :Ahmed Masud
Address (at least 5 chars)    :1999 Somewhere Out there, Bingo, Bongo, 10002, USA
Birthday (YYYY/MM/DD)         :1970/01/10
Telephone (max 16 digits)     :1-201-555-1212

现在让我们以合理的方式将事物复制到我们的结构中

Function to print biodata

/* add a function to print a biodata */

void print_bio(const struct biodata *bio)
{
    printf("***** bio data *****\n"
       "Name: %-10s\nAddress: %-64s\nBirthday: %-10s\nPhone: %-16s\n",
           bio->name, bio->address,
           bio->birthday, bio->phone);
}

new main function

注意大部分都像test_matches。除非我们已将行复制添加到适当的字段

int main()
{

    char line[256];
    struct biodata bio;

    while(!match_name(ask("Name (first last)", line, sizeof(line))))
        ;
    strncpy(bio.name, line, sizeof(bio.name));

    while(!match_address(ask("Address (at least 5 chars)", line, sizeof(line))))
        ;
    strncpy(bio.address, line, sizeof(bio.address));

    while(!match_birthday(ask("Birthday (YYYY/MM/DD)", line, sizeof(line))))
        ;
    strncpy(bio.birthday, line, sizeof(bio.birthday));

    while(!match_telephone(ask("Telephone (max 16 digits)", line, sizeof(line))))
        ;
    strncpy(bio.phone, line, sizeof(bio.phone));

    print_bio(&bio);
    return 0;
}

好的,我们可以提示用户并将内容输入到我们的结构中,但是在main中执行它很笨重,所以让我们将它变成自己的函数。

int get_bio(struct biodata *bio)
{

    char line[256];

    while(!match_name(ask("Name (first last)", line, sizeof(line))))
        ;
    strncpy(bio->name, line, sizeof(bio->name));

    while(!match_address(ask("Address (at least 5 chars)", line, sizeof(line))))
        ;
    strncpy(bio->address, line, sizeof(bio->address));

    while(!match_birthday(ask("Birthday (YYYY/MM/DD)", line, sizeof(line))))
        ;
    strncpy(bio->birthday, line, sizeof(bio->birthday));

    while(!match_telephone(ask("Telephone (max 16 digits)", line, sizeof(line))))
        ;
    strncpy(bio->phone, line, sizeof(bio->phone));

    return 0;
}


int main()
{
    struct biodata bio[3]; /* let's get 3 records */
        int i;

        /* bio is made up of a bunch of struct biodata's so we divide its size by sizeof the struct biodata to get how many (in our case 3) */

        for(i = 0; i < sizeof(bio)/sizeof(struct biodata); i++)
        {
            printf("\n\nEnter record number: %d\n", 1+i); /* why 1+i here ? :) */
            get_bio(&bio[i]);
        }

        for(i=0; i < sizeof(bio)/sizeof(struct biodata); i++)
        {
                print_bio(&bio[i]);

        }
    return 0;
}

Exercise

我将把剩下的功能作为练习。

同时,我希望您考虑我们开发此方法的方式。从最内在的功能开始,慢慢向外移动。

像问题一样分解问题并首先研究内部部件,测试它们是否完全按照你想要的方式进行,然后慢慢围绕它们进行构建。

显然,匹配器应该在开发问题之前单独开发并进行测试。我留给你打破它。

© www.soinside.com 2019 - 2024. All rights reserved.