c - 在 C 中输入 char 字符串的正确输入法
问题描述
我如何最好地在一个程序中输入,该程序要求用户输入用空格分隔的学生姓名,然后输入学生分数,例如:
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] */ );
}
}
解决方案
您type
的 for scores
( ) 与您尝试使用( )int scores[row];
阅读不对应。转换说明符用于转换空格分隔的字符串,而不是整数。为整数转换提供了转换说明符。scores
scanf
scanf("%s", &scores);
"%s"
"%d"
在看细节之前。任何时候你有一个将不同类型的数据协调为一个单元的编码任务(例如,student
每个都有一个name
(char*
)和一个score
(int
)),你应该考虑使用一个struct
包含name
和score
作为成员。这样只有一个需要的结构数组,而不是试图协调多个不同类型的数组以包含相同的信息。
此外,不要吝啬字符数组的缓冲区大小。您宁愿 10,000 个字符太长也不愿 1 个字符太短。如果您认为您的最大名称是 10-16 个字符,请使用 64 字符(或更大)的缓冲区以确保您可以读取整行数据 - 消除输入的一些杂散字符可能导致字符保持未读的可能性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
作为一个单元而不是两个数组,一个char
和一个int
你必须处理。[1]
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
此处)中仍未读取的内容不取决于使用的转换说明符。整行(直到并包括尾随'\n'
)从输入缓冲区中提取并放置在您给fgets
填充的缓冲区中。您还可以检查用户是否只需按下Enter可以方便地指示输入结束的按键,例如
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;
(请注意,您可以(并且应该)另外检查填充的缓冲区的字符串长度(1)检查读取的最后一个字符是否'\n'
确保读取了完整的行;以及(2)如果最后一个字符不'\n'
检查长度是否等于最大长度 ( -1
),表示字符可能未读。(留给您)
现在您知道您有一行输入并且它不是空的,您可以调用sscanf
以将行解析为每个学生的name
and 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;
}
}
如果您注意,您可以从健壮性的角度看到使用fgets
and方法的好处之一。sscanf
您将获得 (1) 读取用户输入的独立验证;(2) 将该输入分离(或解析)为所需的值。任何一种情况下的故障都可以得到适当的处理。
将所有部分放在一个简短的程序中,您可以执行以下操作:
#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 指定以 suffix 结尾的名称
_t
保留供其使用。(size_t, uint64_t, etc...
) 另请注意,您会看到常用的后缀。所以在你自己制作之前检查一下(但我们没有没有 POSIXstudent_t
类型)。
推荐阅读
- java - Mockito 没有返回预期的字符串,而是返回了模拟对象
- android - 呼叫需要权限,可能会被用户拒绝:
- swift - 使用 RxSwift 重放最后一个请求
- security - 移动应用程序的安全漏洞
- php - PHP HTTP_HOST 可以返回值 'localhost' 但 REMOTE_ADDR 是公共 ip 吗?
- aws-sdk - 如何将记录从 NodeJS 应用程序 (lambda) 放入 Kinesis 流
- linux - 如何在用户空间获取/设置 MAX_ZONEORDER 的值?
- linux - 获取与 shell 中的 sed 命令匹配的确切两行之间的行
- ios - 无法使用 james montemagno 的地理定位插件将 DBNull 投射到对象
- python - 如何在管道内使用 SMOTENC(错误:某些分类索引超出范围)?