我正在编写一个程序,接收用户输入的个人详细信息,包括姓名、ID 和电话号码。我将我的 ID 和电话号码分别限制为最多 14 个和 13 个字符(不包括换行符)——例如
010203-1234567
为 ID 和 010-1111-2222
为电话号码。如果我理解正确的话,fgets
应该在从用户输入的字符串中读取后自动添加一个换行符字节,而 fputs
应该在存储在 id
和 phoneNum
中的字符串末尾写入换行符,但是当尝试输入比允许的更多的字符时,例如010203-12345678
对于 ID,以下 fputs
没有写出换行符。我如何确保即使用户输入的字符多于允许的字符数,换行符也由 fputs
写出?
以下是我的代码:
#include <stdio.h>
void FlushInputBuf(void)
{
while (getchar() != '\n');
}
int main(void)
{
FILE *fp = fopen("details.txt", "wt");
if (fp == NULL)
{
puts("File open failed.\n");
return -1;
}
char name[20];
char id[15];
char phoneNum[14];
printf("Enter name: ");
fgets(name, sizeof(name), stdin);
printf("Enter ID: ");
fgets(id, sizeof(id), stdin);
FlushInputBuf();
printf("Enter phone number: ");
fgets(phoneNum, sizeof(phoneNum), stdin);
fputs("#Name: ", fp);
fputs(name, fp);
fputs("#SSID: ", fp);
fputs(id, fp);
fputs("#Phone number: ", fp);
fputs(phoneNum, fp);
fclose(fp);
return 0;
}
我假设输入的名称不会超过 20。 当我输入 ID 为
010203-1234567
和电话号码为 010-1111-2222
时,我在 details.txt 中得到以下结果:
#Name: John
#SSID: 010203-1234567#Phone number: 010-1111-2222
如何让代码写入 details.txt 如下:
#Name: John
#SSID: 010203-1234567
#Phone number: 010-1111-2222
如果我理解正确的话,fgets 应该在从用户输入的字符串中读取后自动添加一个换行符
没有。
fgets()
会将字符复制到缓冲区中,直到它用完空间(同时为字符串终止符保留空间),或者它已经 copied 一个换行符。它不会添加任何不在输入中的换行符,并且(因此)它不能确保字符串末尾有换行符。通常有一个,但是如果您的缓冲区对于整条线来说太小,那么就不会有。这是判断您是否有完整行的主要方法。
fputs 应该在存储在 id 和 phoneNum 中的字符串末尾写入换行符
是的,
fputs
将写入出现在指定字符串中的任何换行符。没有被打印是有力的证据表明没有人被读入你的id
(和phoneNum
)缓冲区。
当尝试输入超过允许的字符时,例如ID 为“010203-12345678”,以下 fputs 没有写出应有的换行符。
它不会像您预期的那样打印换行符。但它确实在做它应该做的事。
您的
id
数组有 15 char
s 长,足以容纳 14 个字符的 ID 和一个空终止符。因此,fgets
在复制第 14 个字符“7”后停止读取,并以缓冲区的最后一个字节终止字符串。您的 FlushInputBuf()
函数然后读取并丢弃行尾,直到并包括下一个换行符。不存储换行符。同样的事情也会发生在恰好 14 个字符的条目上。如果输入的 ID 比那个短,那么 FlushInputBuf()
调用将强制您在提示输入电话号码之前输入第二个换行符(并且会丢弃您之前输入的任何其他内容)。
这样做的好方法是
提供一个至少足够长的缓冲区来容纳预期的输入,加一个换行符,加一个字符串终止符.
#define MAX_ID_LENGTH 14
// ...
char id[MAX_ID_LENGTH + 2];
有时允许比您实际期望的多一点是有利的。
fgets()
调用后,检查缓冲区中的换行符。仅当有任何输入行的尾部时才使用 - 也就是说,如果尚未读取换行符。
size_t newline_pos = strcspn(id, "\n");
if (id[newline_pos] != '\0') {
assert(id[newline_pos] == '\n');
FlushInputBuf();
}
验证或至少修复输入。特别是,删除换行符(如果存在),因为它实际上不是 ID 的一部分。
id[newline_pos] = '\0';
拒绝或截断过长的 ID。
// maybe:
id[MAX_ID_LENGTH] = '\0';
执行任何其他适当的验证。
在你想要的地方手动打印换行符到输出。在您的特定情况下,我会使用一个
fprintf()
调用来代替每对 fputs()
调用,并在格式中适当放置换行符。
fprintf(fp, "#SSID: %s\n", id);
但是如果你必须只使用
fputs()
,那么你可以添加一个fputs("\n", fp);
。
如果您不关心空头条目的影响(您应该关心),那么这一步本身就足够了。
除了@JohnBollinger 的出色响应,这里是您的代码的修改版本,使用辅助功能控制用户输入:
#include <errno.h>
#include <stdio.h>
#include <string.h>
int input_string(const char *prompt, char *dest, int size) {
int c; // byte read from stdin
int i = 0; // index into dest
int n = 0; // number of bytes read excluding the newline
printf("%s", prompt);
/* ensure prompt is visible (necessary on some older systems) */
fflush(stdout);
/* read all bytes from the user input line */
while ((c = getchar()) != EOF && c != '\n') {
if (i + 1 < size) {
/* append the character to dest, space permitting */
dest[i++] = (char)c;
}
n++;
}
if (size > 0) {
// set the null terminator
dest[i] = '\0';
}
if (n >= size) {
printf("discarded %d extra characters\n", n - size - 1);
}
if (c == EOF) {
if (i == 0)
printf("premature end of file\n");
return EOF;
} else {
printf("missing newline at end of file\n");
}
}
/* return the number of characters stored into dest */
return i;
}
int main(void) {
FILE *fp = fopen("details.txt", "w");
if (fp == NULL) {
fprintf(stderr, "Failed to open %s: %s\n", "details.txt",
strerror(errno));
return 1;
}
char name[20];
char id[15];
char phoneNum[14];
if (input_string("Enter name: ", name, sizeof(name)) < 0
|| input_string("Enter ID: ", id, sizeof(id)) < 0
|| input_string("Enter phone number: ", phoneNum, sizeof(phoneNum)) < 0) {
fprintf(stderr, "Missing input\n");
fclose(fp);
return 1;
}
fprintf(fp, "#Name: %s\n", name);
fprintf(fp, "#SSID: %s\n", id);
fprintf(fp, "#Phone number: %s\n", phoneNum);
fclose(fp);
return 0;
}