gpt4 book ai didi

c - 程序跳过用户输入的机会,使用 fgets 和 sscanf

转载 作者:太空宇宙 更新时间:2023-11-04 08:33:46 25 4
gpt4 key购买 nike

我正在编写一个程序,以便用户可以创建一个文本文件来存储他们梦想中的汽车列表、车库库存、任何车辆列表。我使用 fgets 和 sscanf 向用户询问品牌、年份、型号、值(value)和颜色。但由于某种原因,它跳过了获取模型的用户输入阶段并跳转到询问值,但仍然在用于输入值的 printf 上方显示“输入汽车型号:”的 printf。下面是我的代码摘录,其中包含我的 fgets/sscanf 函数,下面是输出示例,因此如果(哈哈,如果)我的描述不是很清楚,您可以看到问题所在。

if (!listappend){
printf("File nonexistent/inaccessible.");
return 1;
}
memset(colorin, 0, sizeof(colorin));
memset(makein, 0, sizeof(makein));
memset(modelin, 0, sizeof(modelin));
memset(yearin, 0, sizeof(yearin));
memset(valuein, 0, sizeof(valuein));


printf("\nEnter car make: ");
fgets(makein, sizeof(makein), stdin);
sscanf(makein, "%[^\n]",makein);
fprintf(listappend, "\n%s", makein);
printf("\nEnter car manufacture year: ");
fgets(yearin, sizeof(yearin), stdin);
sscanf(yearin, "%d", &yearinf);
printf("\nEnter car model: ");
fgets(modelin, sizeof(modelin), stdin);
sscanf(modelin, "%[^\n]",modelin);
fprintf(listappend, "\n%s", modelin);
printf("\nEnter approximate car value: $");
fgets(valuein, sizeof(valuein),stdin);
sscanf(valuein, "\n%f", &valueinf);
fprintf(listappend, "\n%d %'.2f", yearinf, valueinf);
printf("\nEnter car color: ");
fgets(colorin, sizeof(colorin), stdin);
sscanf(colorin, "%[^\n]",colorin);
fprintf(listappend, "\n%s", colorin);

fclose(listappend);

样本输出:
|-|-|-|-|-|-|-|-|-|-|Car-lection List|-|-|-|-|-|-|-|-|-|-|
----------------------------------------------------------
[A]dd New Car [V]iew List
[M]enu [D]eveloper Info
[C]lear List [Q]uit

Option?
A

Enter car make: Honda

Enter car manufacture year: 2000

Enter car model:
Enter approximate car value: $5000

Enter car color: Blue

Option?
V
Entry Year Color Make Model Color Approx. Value
1| 2000 Blue Honda, approximate value: $5,000.00

如您所见,用户应该可以输入车型,但程序会立即询问大致的车型值(value),从而跳过了输入车型信息的机会。
这是 car_list.txt 的猫,我保存要读取的字符串和值的文本文件:
    cat car_list.txt

Honda


2000 5,000.00
Blue

我是 C 编程的新手,所以如果这是一个愚蠢的错误,我很抱歉浪费了你的时间,但这让我发疯了。预先感谢您的帮助!

最佳答案

调用 sscanf()不正确:

sscanf(makein, "%[^\n]", makein);

这会导致未定义的行为。 sscanf() 的正式 POSIX(和 C99、C11)规范是:
int sscanf(const char *restrict s, const char *restrict format, ...);
restrict表示对 sscanf() 的调用中可能没有任何其他参数这是 s 的别名论点(或与之重叠)。在您的代码中,您有 makein作为源字符串和目标字符串,这违反了 restrict标准,因此导致未定义的行为。

代码中的重复要求使用函数。

您没有向我们展示您定义的字符串的大小。您也从未检查过 fgets()成功了,也不是 sscanf()那是成功的。我怀疑您的字符串之一太短,因此 fgets()实际上并没有读取整行,而“跳过”读取的是上一行的最后一部分。您可以通过打印输入数据来诊断这一点。例如,在每个(成功) fgets() 之后,您可以打印:
printf("<<%s>>\n", makein);

这将确保程序已经看到了您希望它看到的输入——这是最基本的调试技术之一。

So you're saying I need to setup a new variable for each sscanf that repeats the first parameter as the third as well? ex of what I'm trying to say: sscanf(makein, "%[^\n]", &makeins) rather than sscanf(makein, "%[^\n]", makein)?



或多或少,是的。

有两个问题需要解决。一是你原来的电话给 sscanf()产生未定义的行为——这很容易。另一部分是您假设每行上的数据都适合变量 - 由于您没有显示大小,因此很难知道这是否合理。也不清楚所有字段的大小是否相同。我的假设是它们的大小不同。

有几种方法可以解决这个问题。我可能会按照以下方式编写一个函数:
int read_value(const char *prompt, char *buffer, size_t buflen)
{
char line[4096]; // Big!

buffer[0] = '\0'; // Null terminate output in case of EOF.
printf("%s: ", prompt);
fflush(stdout); // Optional
if (fgets(line, sizeof(line), stdin) == 0)
return EOF;
size_t len = strlen(line);
if (line[len-1] == '\n')
line[--len] = '\0';
if (len > buflen)
len = buflen - 1;
memmove(buffer, line, len);
buffer[len] = '\0';
return len;
}

这假设数据的截断是可以的,并且不需要跳过前导空格,也不需要跳过除换行符之外的尾随空格。删除前导空格很简单: strspn()是理想的。删除尾随空格不那么简单;您必须自己编写递减循环,注意正确处理全空行。

该函数打印提示字符串并在末尾添加冒号和空格;如果你不想这样,请使用 printf("%s", prompt);fputs(prompt, stdout); — 不要使用 printf(prompt);因为如果提示曾经包含百分号,那会严重崩溃: "Enter discount (%)" , 例如。 fflush()通常不需要(系统通常会为您执行此操作),但它确保提示可见。
fgets()读入一个大的行缓冲区;出于所有实际目的,它确保您阅读整行。换行符的测试可以给出 else处理截断行的子句——JSON 数据(例如书签文件)可以是巨大的单行数据——你可以报告错误,或者吞噬到行尾或 EOF,或者任何其他适合你的机制。

如果读取的数据不适合传递给函数的缓冲区,该代码还会截断它 - 同样,如果您愿意,您可以报告错误,或者使用更复杂的截断算法而不是简单地以最大长度覆盖数据(例如,搜索前面的分词)。
memmove()加上赋值 null 终止复制后的数据。返回字符串的长度(或错误时的 EOF)。这是 fgets() 的更好版本会自动执行(并且 POSIX getline() 函数已经执行)。

使用此函数,您的代码如下:
printf("\nEnter car make: ");
fgets(makein, sizeof(makein), stdin);
sscanf(makein, "%[^\n]",makein);

会成为:
if (read_value("\nEnter car make", makein, sizeof(makein)) == EOF)
…handle error…

您现有的代码忽略了 EOF,因此这是一个改进。我还注意到您的 memset()通话并不是真正必要的;返回的字符串将以空值结尾。

关于c - 程序跳过用户输入的机会,使用 fgets 和 sscanf,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27162477/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com