我最好如何在程序中输入以下信息:要求用户输入用空格隔开的学生姓名,然后输入学生成绩EX:
zach 85
由于使用空终止符,我是否必须考虑两次输入?我已经在程序中使用了两个scanfs。
int main()
{
const int row = 5;
const int col = 10;
int i;
char names[row][col];
int scores[row];
int total;
// Read names and scores from standard input into names and scores array
// Assume that each line has a first name and a score separated by a space
// Assume that there are five such lines in the input
// 10 points
for(int i = 0; i<row; i++)
{
printf("Enter student name: \n");
scanf("%s",&names);
scanf("%s", &scores);
}
// Print the names and scores
// The print out should look the same as the input source
// 10 points
for(int i = 0; i<row; i++)
{
printf( "%s %d \n", names[i] /*, scores[i] */ );
}
}
答案 0 :(得分:4)
您对type
(scores
)的int scores[row];
与您尝试用scores
(scanf
)阅读scanf("%s", &scores);
的尝试不符。 "%s"
转换说明符用于转换由空格分隔的字符串,而不是整数。 "%d"
转换说明符用于整数转换。
在查看细节之前。任何时候,您都需要将不同类型的数据作为一个单元进行协调(例如,student
和name
(char*
)和score
({{ 1}}),您应该考虑使用包含int
和struct
作为成员的name
,这样只有一个 struct数组而不是尝试协调不同类型的多个数组以包含相同的信息。
另外,不要忽略字符数组的缓冲区大小。您宁愿10,000个字符太长而不是1个字符太短。如果您认为最大名称是10到16个字符,请使用64个字符(或更大)的缓冲区,以确保您可以读取整行数据-消除了键入一些杂散字符可能导致在{ {1}}。
仅需一个简单的score
。为了方便起见,您可以添加stdin
(以避免为每个声明或参数键入stuct
),例如
typedef
现在您有了一个结构,其中将您的学生struct name_of_struct
和 #include <stdio.h>
#define ROW 5 /* if you need a constant, #define one (or more) */
#define COL 64
typedef struct { /* delcare a simple struct with name/score */
char name[COL]; /* (add a typedef for convenience) */
int score;
} student_t;
包含为一个单元而不是两个数组,一个数组name
和一个score
必须处理。 [1]
剩下的就是声明要在您的代码中使用的char
数组,例如
int
在声明了struct数组之后,您可以转向输入处理。处理输入的一种可靠方法是连续循环,验证您是否在每次迭代中收到期望的输入,处理出现的任何错误(优雅地使您的代码继续),并跟踪输入的数量,以便您可以保护数组边界,并通过在数组末尾书写来避免调用未定义行为。
您可以开始输入循环,如我的评论中所述,使用student_t
提示和阅读输入行。与尝试使用int main (void) {
int n = 0; /* declare counter */
student_t student[ROW] = {{ .name = "" }}; /* array of struct */
puts ("\n[note; press Enter alone to end input]\n");
进行每个输入相比,这具有多个优点。最值得注意的是,输入缓冲区(此处为fgets
中尚未读取的内容不取决于所使用的转换说明符。从输入缓冲区中提取整行(直至尾随scanf
),并将其放置在您给stdin
填充的缓冲区中。您还可以检查用户是否仅按下 Enter ,即可方便地指示输入结束,例如
'\n'
(请注意,您可以(并且应该)另外检查填充到缓冲区的字符串长度,以(1)检查最后读取的字符为fgets
,以确保读取了整行;以及(2)如果最后一行char不是 for (;;) { /* loop until all input given or empty line entered */
char buf[COL]; /* declare buffer to hold line */
fputs ("Enter student name: ", stdout); /* prompt */
if (!fgets (buf, sizeof buf, stdin)) /* read/validate line */
break;
if (*buf == '\n') /* check for empty line */
break;
检查长度是否等于最大长度('\n'
),以表示可能未读字符(留给您)
现在您知道自己有一行输入并且它不是空的,您可以调用'\n'
将每个学生的行解析为-1
和sscanf
,同时处理任何失败的情况优雅地进行转换,例如
name
如果您关注的是,从健壮性的角度来看,您会发现使用score
和 /* parse line into name and score - validate! */
if (sscanf (buf, "%63s %d", student[n].name, &student[n].score) != 2)
{ /* handle error */
fputs (" error: invalid input, conversion failed.\n", stderr);
continue;
}
n++; /* increment row count - after validating */
if (n == ROW) { /* check if array full (protect array bounds) */
fputs ("\narray full - input complete.\n", stdout);
break;
}
}
方法的好处之一。您将获得以下方面的独立验证:(1)用户输入的读取; (2)将该输入分离(或解析)为所需值。两种情况下的失败都可以得到适当处理。
将所有内容放到一个简短的程序中,您可以执行以下操作:
fgets
使用/输出示例
每次编写输入例程时-尝试并破坏它!。故意输入无效数据。如果您的输入例程中断-请修复它!。在上述代码中,您唯一要实现和处理的检查就是输入的字符数大于sscanf
(例如键盘上的猫脚步)。锻炼您的输入:
#include <stdio.h>
#define ROW 5 /* if you need a constant, #define one (or more) */
#define COL 64
typedef struct { /* delcare a simple struct with name/score */
char name[COL]; /* (add a typedef for convenience) */
int score;
} student_t;
int main (void) {
int n = 0; /* declare counter */
student_t student[ROW] = {{ .name = "" }}; /* array of struct */
puts ("\n[note; press Enter alone to end input]\n");
for (;;) { /* loop until all input given or empty line entered */
char buf[COL]; /* declare buffer to hold line */
fputs ("Enter student name: ", stdout); /* prompt */
if (!fgets (buf, sizeof buf, stdin)) /* read/validate line */
break;
if (*buf == '\n') /* check for empty line */
break;
/* parse line into name and score - validate! */
if (sscanf (buf, "%63s %d", student[n].name, &student[n].score) != 2)
{ /* handle error */
fputs (" error: invalid input, conversion failed.\n", stderr);
continue;
}
n++; /* increment row count - after validating */
if (n == ROW) { /* check if array full (protect array bounds) */
fputs ("\narray full - input complete.\n", stdout);
break;
}
}
for (int i = 0; i < n; i++) /* output stored names and values */
printf ("%-16s %3d\n", student[i].name, student[i].score);
}
虽然您可以使用两个单独的数组,但使用单个结构数组是一种更好的方法。仔细研究一下,如果您还有其他问题,请告诉我。
脚注:
COL
结尾的名称保留供其使用。 ($ ./bin/studentnamescore
[note; press Enter alone to end input]
Enter student name: zach 85
Enter student name: the dummy that didn't pass
error: invalid input, conversion failed.
Enter student name: kevin 96
Enter student name: nick 56
Enter student name: martha88
error: invalid input, conversion failed.
Enter student name: martha 88
Enter student name: tim 77
array full - input complete.
zach 85
kevin 96
nick 56
martha 88
tim 77
)另外请注意,您将看到通常使用的后缀。因此,请先检查一下,然后再构成自己的(但我们没有POSIX _t
类型)。答案 1 :(得分:1)
您快到了。但是,您必须确保自己做事整洁,让我们逐步进行操作:
#include <stdio.h>
#define MAX_NAME_LENGTH 30
int main() {
char name[MAX_NAME_LENGTH+1]; /* an array of characters making up ONE name (+1 for terminating NUL char in case of max-length name) */
unsigned int score; /* a score */
scanf("%30s", name); /* scan a name (assuming NO spaces in the name)*/
/* also notice that name has no & in front of it because it already IS a pointer to the array name[MAX_NAME_LENGTH] */
scanf("%u", &score);
printf("%s scored %u in the test\n", name, score);
return 0;
}
(看到它在http://tpcg.io/jS3woS上运行)
现在让我们阅读5对,然后打印5对。
#include <stdio.h>
#define MAX_NAME_LENGTH 30
/* i called rows iterations here just to provide contrast */
/* you can call them ROWS if you want but it then creates a confusion about name length */
#define ITERATIONS 5
int main() {
char name[ITERATIONS][MAX_NAME_LENGTH+1]; /* an array of names where each name is MAX_NAME_LENGTH long (notice the order) */
unsigned int score[ITERATIONS]; /* score */
int i;
for(i = 0; i < ITERATIONS; i++ ) {
scanf("%30s", name[i]); /* scan a name (assuming NO spaces in the name)*/
/* notice that name[i] has no & in front of it because name[i] is the pointer to the i-th row */
scanf("%u", &score[i]);
}
for(i = 0; i < ITERATIONS; i++ ) {
printf("%s scored %u in the test\n", name[i], score[i]);
}
return 0;
}
在此处查看实际操作(http://tpcg.io/iTj4ag)
答案 2 :(得分:0)
第一眼scores
和names
被定义为数组,所以
scanf("%s",names[i]);
scanf("%s", &scores[i]);
第二scores
是int
,所以"%d"
而不是"%s"
scanf("%s",names[i]);
scanf("%d", &scores[i]);
第三,您已经定义了int i;
,所以for loop
中的那个并没有任何意义,只能在一个地方进行。
第四,如果您输入的名称包含spaces
scanf
不是正确的选择
来自scanf
的手册页
Each conversion specification in format begins with either the character '%' or the character sequence "%n$" (see below for the distinction) followed by:
· An optional decimal integer which specifies the maximum field width. Reading of characters stops either when this maximum is reached or when a non‐
matching character is found, whichever happens first. Most conversions discard initial white space characters (the exceptions are noted below), and
these discarded characters don't count toward the maximum field width. String input conversions store a terminating null byte ('\0') to mark the end
of the input; the maximum field width does not include this terminator.
您可能想将scanf期间要扫描的字符数限制为某些限制示例“ %20s"