我遇到scanf()
的问题。我从阅读论坛之类的书中知道scanf()
在C语言中是很成问题的,但是我仍在学习基础知识,所以我不知道所有的细节。
#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)
时,输入保持不变。但是,我确实需要scanset,因此我可以输入空格,并在scanf()
中输入空格时弹出错误消息。
答案 0 :(得分:4)
您误以为[
和%s
的修饰语,例如%d
-不是。 %3d
本身是一个转换说明符,其作用类似于%[
。
因此,正如@ user3629249的注释所指出的,%s
末尾的s
和d
(例如%[
中)是多余的。 %[^\n A-Z a-z]s
中的空格也很重要。因此%[
与%[A-z a-z]
让我们看看在打开格式警告时遇到的问题。 (-Wformat,如果您使用的是gcc或clang),则会得到类似的内容:
%[A-Za-z]
您的代码还有其他问题:
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) {
~~~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~
进行循环),但是它被声明为大小为10 index_struct
的数组在C中,数组基于Biodata bio[10];
,因此它们从0
到0
,并且您的 for循环会因为{{ 1}}是不确定的。
您要在自己的for循环内size-1
内请求,但您需要for循环。
如果input_max大于声明的bio[10]
数组大小,会发生什么?
要考虑的其他一些好东西:
printf是用于报告错误的错误函数,错误应该传递给stderr,而不是stdout,因此最好使用fprintf并指定stderr。
由于您有兴趣确保正确解析输入,为什么不创建自己的解析器而不是使用scanf?
您要强制再次提示错误,让我们将其分解为自己的功能。
我的C风格与您的C风格略有不同,我有我的理由:-),由于这只是观点,所以让我来看一下。
input_max
下面是一组匹配函数,它接收输入行并告诉我们该行是否完全符合我们的期望。如果这样做,则返回true,否则返回false。您需要为此bio
。
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 */
};
测试一下。
#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
。除了我们添加了
线到适当的字段
/* 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);
}
好的,我们可以提示用户并将其放入我们的结构中,但主要是很难做到的,所以让我们将其纳入自己的功能中。
test_matches
我将其余功能保留为练习。
同时,我希望您考虑我们开发此方法的方式。从最内部的功能开始,然后逐渐向外移动。
消除诸如乐高积木之类的问题,并首先对内部零件进行测试,测试它们是否完全按照您的要求完成,然后慢慢围绕它们进行构建。
很明显,匹配器应该是单独开发的,并在进行Ask之前经过测试。我把它交给你来解决。