【问题标题】:Line wise input in cc中的逐行输入
【发布时间】:2015-08-08 05:43:51
【问题描述】:

如何在 c 中强制执行以下输入限制?

第一行包含 float , 第二行包含 float , 第三行 int,

在控制台中按三下回车后,程序应该能够读取每一行并将内容放入相应的 int,int,float 变量中。

三个回车键后程序不应等待用户输入并开始验证。

一些测试用例

line1: 34
line2:4
line3:12
result: ok

line1: 
line2:4
line3:12
result: not ok 

line1: Hi
line2:4
line3:12
result:  not ok 

到目前为止,我使用的是基础知识

    scanf("%f",&p);
    scanf("%f",&r);
    scanf("%d",&t);

它适用于测试用例 1 和 3,但当我留下空行时会失败。

【问题讨论】:

  • 你必须用scanf做这个吗?
  • 阅读带有fgets 的行。使用sscanfstrto_ 系列函数进行转换。
  • 我不允许使用 conio.h。仅 stdio.h :(
  • fgets -> 修剪 -> sscanf%n
  • 这些函数都不在 中。 fgetssscanf 中。 strto_

标签: c validation input scanf


【解决方案1】:

您应该始终检查scanf的返回值。

原因是返回值是scanf 用来传达转换错误以及其他错误的内容。例如,如果您的程序告诉 scanf 需要一个十进制数字序列,而 scanf 遇到与该模式不匹配的内容,则返回值将指示此失败。

返回的值将是成功分配的项目数。例如,

char str[128];
int x, y = scanf("%d %127s", &x, str);

如果y 为1,则应假定x 可以安全使用。如果y 为2,那么应该假设xstr 都可以安全使用。

这回答了您的部分问题。下一部分是如何确保输入是 lines 的形式。 scanf 不严格处理;它处理其他单位,例如%d 是编码为十进制数字序列(和符号)的int;它会在十进制数字序列结束后返回...无法保证十进制数字将占据整行

这里实际上有两个问题:前导尾随空格。除了[cCn 之外,所有格式说明符都会导致前导空格被丢弃。如果您想以不同的方式处理前导空格,则需要编写您期望如何处理前导空格。

考虑到丢弃用户输入几乎总是(如果不是总是)一个坏主意。如果您不关心该行的其余部分包含什么,您可以使用 scanf("%*[^\n]"); getchar(); 之类的东西来丢弃所有尾随并包括 '\n' 换行符的所有内容...第一个语句将尝试读取尽可能多的非换行符尽可能,第二个将丢弃终止换行符。但是,如果要保证输入占满整行,则需要测试getchar返回的值。

使用所有这些考虑因素的示例:

/* Test for leading whitespace (including newlines) */
int c = getchar();
if (c != '-' && !isdigit(c)) {
    /* Leading whitespace found */
}
ungetc(c);

/* Test for correct data conversion */
int x, y = scanf("%d", &x);
if (y != 1) {
    /* Something non-numeric was entered */
}

/* Test for trailing newline */
c = getchar();
if (c != '\n') {
    /* Trailing newline found */
}

有了这些信息,如果您有任何问题,也许您可​​以尝试并用一些代码更新您的问题......

附:我注意到在您编写的代码中,您似乎对%f%d 感到困惑; %f 用于读入floats,%d 用于读入ints,而不是相反...

【讨论】:

  • 拒绝输入初始或尾随空格是个坏主意。许多应用程序忽略它们是很常见的,因此用户习惯于随意放置它们。采取不同的做法只会带来糟糕的用户体验。
  • @SergeBallesta 当然。正如我已经写过的,“考虑到丢弃用户输入几乎总是(如果不是总是)一个坏主意。”但是,如果您查看问题中的示例,则 OP 希望空白 lines 输入失败;需要自定义处理前导空格,这是底线。
【解决方案2】:

当我阅读line wise input时,我就知道必须使用fgets + sscanf 而不是直接使用scanf。当然,您可以使用getc/getchar 作为解决方法,但您可以获得极端情况,我在其中找到fgets + sscanf 清洁剂。在一行上单独获取浮点数的示例:

char line[80], dummy[2];
float val;
if (fgets(line, sizeof(line), stdin) == NULL)... 
if (sscanf(line, "%f%1s", &val, dummy) != 1)...
// Ok val was alone on the line with optional ignored blanks before and/or after

您还可以为 loooong 行添加测试:

if ((line[0] != 0) && (line[strlen(line)-1 != '\n'))...

【讨论】:

  • float val; int n = scanf("%f", &val); scanf("%*[^\n]"); getchar(); if (n != 1) { /* some error handling */ } 更干净吗?为 loooong 行 添加测试会使您的代码更加混乱,超出您的想象。当fgets遇到比sizeof(line)字符长的行时,您需要考虑,例如...您可以使用scanf("%*[^\n]"); getchar();,但随后您将使用scanf...这很愚蠢,对吧?为什么要使用最合适的工具来完成这项工作?
  • @Freenode-newbostonSebivor 您的代码确实要短得多......并且在同一行上默默地接受 3 个浮点数,这是 OP 禁止的。如果您不需要面向行的输入,fgets 矫枉过正。但如果你需要它,它是更健壮的方式。
  • 该代码不会“在同一行静默接受 3 个浮点数”...int n = scanf("%f", &val); 会将值读入val,然后scanf("%*[^\n]"); getchar(); 将丢弃该行的其余部分输入,这是单独使用fgets+sscanf 无法干净可靠地完成的事情,因为当一行比sizeof(line) 长时,它将将该行拆分为两个读取。当发生这种情况时,您可以使用我用来丢弃该行的其余部分的相同代码,但是您也可以只使用scanf 而不是fgets+sscanfgetchar 可以做到这一切,但代价是混乱。
  • 有一件事我不明白,那就是......尽管fgets + sscanf 的问题在使用scanf + getchar 时不存在,@ 987654350@ 被认为“更健壮”?
  • fgets + sscanf 忽略前导或尾随空格(可以是空格、制表符或\r)并拒绝空行,因为您每行只要求一个值。使用scanf + getchar,当您想忽略空格或制表符时,程序员必须明确处理它们。当您阅读文本文件时,您能否立即在行尾看到尾随空格或制表符,甚至是额外的\r
猜你喜欢
  • 2014-01-07
  • 1970-01-01
  • 2013-09-17
  • 2018-01-08
  • 2020-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多