【问题标题】:How to ignore floating number in scanf("%d")?如何忽略scanf(“%d”)中的浮点数?
【发布时间】:2013-08-22 18:13:40
【问题描述】:

如果用户为整数变量输入浮点数,我想打印无效输入。这可能吗?

 int a;
 scanf("%d",&a); // if user enters 4.35 print invalid input 

我试过这样的角色

  if(scanf("%d",&a)==1);
  else printf("invalid input");

但是对于浮点数怎么办。如果用户输入 4.35 它会截断为 4 但我想要无效的输入。

【问题讨论】:

标签: c scanf


【解决方案1】:

由于小数点前任何数字的浮点数的开头看起来像一个整数,因此无法单独使用%d 检测到这一点。

您可以考虑使用fgets() 阅读整行,然后使用sscanf() 进行分析:

int a;
int n;
char line[4096];
if (fgets(line, sizeof(line), stdin) != 0 && sscanf(line, "%d%n", &a, &n) == 1)
   ...analyze the character at line[n] for validity...

(是的,我的意思是与 1 进行比较;%n 转换规范不计入来自 sscanf() 等的返回值中。)

scanf() 做的但此代码没有做的一件事是在输入数字之前跳过空白行。如果这很重要,您必须编写一个循环来读取(非空)行,然后解析非空行。您还需要确定线路上可以容忍多少尾随垃圾(如果有)。允许空格吗?标签?字母字符?标点符号?

【讨论】:

  • 是不是有点过于复杂了?
  • 这取决于你想要什么。使用fgets()sscanf()一般比scanf()好;报告用户以这种方式输入的内容会更容易。因此,将其排除在解决方案之外。剩下sscanf(line, "%d%n", &a, &n) == 1 并检查您是否没有小数点(默认为.,但通常特定于区域设置),或者eE 作为下一个字符;其他任何内容都表示输入的值是整数。即使E 也不是严格证明;你需要看到E 和一个数字。 1.2E 是数字 1.2,后面有一个 E,AFAIK(虽然我今天没有测试过)。
  • 有趣:在 Ubuntu 12.04 衍生版本上,以下代码将 E 视为浮点数的一部分;它打印出空格 (32) 作为停止点。 #include <stdio.h> int main(void) { char data[] = "34.1E and junk"; double d; int n; if (sscanf(data, "%lf%n", &d, &n) != 1) printf("scan failed\n"); else printf("Got: %f (stopped at '%c' %d)\n", d, data[n], data[n]); return 0; }我不完全相信这是正确的行为;在 Mac OS X 上,转换在 E 上停止(我认为是正确的)。 ("34.1E- and junk" 在 Linux 上也停在空白处。)
  • 实际上,在 C99 之前,未指定匹配 %n 是否计入返回值——因此在某些系统上成功匹配可能会返回 2。没关系,因为您可以使用 sscanf(line, "%d%n", &a, &n) >= 1 轻松捕获这种情况,因为匹配失败将始终返回 0。
  • @thejh C 中的健壮 I/O 很复杂!
【解决方案2】:

您必须将其读取为双精度,然后检查它是否为整数。检查它是否为整数的最佳方法是使用modf,它返回双精度的小数部分。如果有一个你有一个错误:

double d;
scanf("%lf", &d);

double temp;
if(modf(d, &temp)){
  // Handle error for invalid input
}

int a = (int)temp;

这将允许小数点后只有0s 的整数或浮点数,例如54.00000。如果您也想将其视为无效,则最好逐个字符地阅读并验证每个字符是否在 09 之间(ascii 48 到 57)。

【讨论】:

  • 不需要检查范围,d 应该在int 范围之外吗?
  • 在许多 64 位机器上,具有 64 位范围的 int 超过了 double 的精度,因此将不正确的值扫描到“a”中。 long double 可能可以工作。
  • 没有多少 64 位机器使用 64 位 int 大小;它让您对如何处理 32 位整数(或 16 位整数)感到困惑。但是,如果您正在处理 long long(或 64 位 long),那么您的观点将完全有效。
  • @Jonathan Leffler int 的范围可能通常double 的整数可表示范围内。但是“通常”不是便携式的想法。我更喜欢可移植的想法,这个解决方案有一个弱点。我曾在 doublefloat 相同的嵌入式领域工作过。即使 int 仍然是 32 位,这也会限制此解决方案。
【解决方案3】:

这不能通过阅读int 来查看停止扫描的原因。

经典成语

char buf[100];
if (fgets(buf, sizeo(buf), stdin) == NULL) {
  ; // deal with EOF or I/O error
}
int a;
char ch;
if (1 != sscanf(buf, "%d %c", &a, &ch)) {
  ; // Error: extra non-white space text
}

【讨论】:

  • 如果整数后面有空格,则为整数。如果用户输入了34 .34. 之前有一个空格),他们首先输入了一个有效的整数;就在它之后,只有尾随的垃圾。当然,这可能是您所追求的……任何尾随的垃圾都是错误的。从问题上看不清楚。
  • @Jonathan Leffler "34 .34" 会失败,因为额外的 IMO 否定了输入的正确性。正如您所说,OP 没有指定尾随/前导空格或额外文本。
【解决方案4】:

这个比较简单:

#include <stdio.h>

int main() 
{
    int a;
    long double b;

    scanf("%f",&b);
    a = (int) b;

    a == b ? printf("%d\n",a) : printf("Invalid input!");

    return 0;
} 

输入:4
输出:

4

输入:4.35
输出:

 Invalid input

【讨论】:

  • 许多 台机器上,如果 'b' > 16,777,216,则将文本扫描到浮点数会丢失如果直接读取 int 则不会发生的精度。跨度>
  • 不清楚unsigned long long bit 中的“位”。请澄清。
  • @chux;哎呀!它的unsigned long long a;
  • 如果输入“16777217”,b 可能会得到值 16,777,218.0。如果“a”是intunsigned long long,则会为float 中的限制分配错误的值,而不是a
  • @Grijesh Chauhan 我发布了一个答案并评论了其他候选人的改进。您建议进行哪些改进?
【解决方案5】:

这是一个简单的方法:

#include <stdio.h>

int main(int argc, char **argv) {
    int d;
    printf("Type something: ");

    // make sure you read %d and the next one is '\n'
    if( scanf("%d", &d) == 1 && getchar() == '\n' ) {
        printf("%d\n", d);
    }

    return 0;
}

.

$ a.exe
Type something: 312312.4214

$ a.exe
Type something: 2312312
2312312

$ a.exe
Type something: 4324.

$

【讨论】:

  • 显示一些测试用例!你试过了吗?
【解决方案6】:

首先,scanf 没有任何问题。当用户输入一个浮点数时,他们实际上输入了一个数字点号。因此,编写一个 scanf 代码来检测该数据条目。

main()
  {
       char c1[2];
       int num1;

       int nr_nums;

       nr_nums = scanf("%d%1[.e0123456789]", &num1, &c1);

       if (nr_nums == 1) {printf("\ndata = %d", num1);}
       if (nr_nums == 2) {printf("\nInvalid");}

 }

根据注释建议的另一种可能的数据输入格式 1. 或 3e-1 修改了此代码。

此代码可以满足您的基本要求。它接受整数数据输入并检测何时输入浮点数。

【讨论】:

  • 但是,如果他们使用科学计数法输入浮点数,这将不起作用,例如1e-5,并且在小数点右侧不带任何内容写入 1. 是在 C 中编写浮点常量的一种完全合法的方式,有人可能会这样输入它并期望它被识别。
  • @PaulGriffiths,问题是用户是否输入了 n.m 而不是您的示例。我会考虑您的要求并找出可以相应修改的 scanf 。 IE。不同的要求,不同的答案!
  • @PaulGriffiths,已修改代码以处理您建议的可能性。
【解决方案7】:

您可以使用 strtol()strtod() 并比较结束指针,例如这个:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char buffer[100];
    char * endptr_n;
    char * endptr_d;
    long n;
    double d;

    fgets(buffer, 100, stdin);
    n = strtol(buffer, &endptr_n, 10);
    if ( endptr_n == buffer ) {
        fputs("You didn't enter a number.", stderr);
        return EXIT_FAILURE;
    }

    d = strtod(buffer, &endptr_d);

    if ( *endptr_d == '\0' || *endptr_d == '\n' ) {
        if ( endptr_d == endptr_n ) {
            puts("You entered just a plain integer.");
        } else {
            puts("You entered a floating point number - invalid.");
        }
    } else {
        puts("You entered garbage after the number - invalid.");
    }

    return EXIT_SUCCESS;
}

输出:

paul@local:~/src/c$ ./testint
2
You entered just a plain integer.
paul@local:~/src/c$ ./testint
2.3
You entered a floating point number - invalid.
paul@local:~/src/c$ ./testint
3e4
You entered a floating point number - invalid.
paul@local:~/src/c$ ./testint
4e-5
You entered a floating point number - invalid.
paul@local:~/src/c$ ./testint
423captainpicard
You entered garbage after the number - invalid.
paul@local:~/src/c$

它不使用scanf(),但这是一件好事,它避免了手动检查您读取的整数之后的输入。

显然,如果在线上唯一的东西是数字,那么很多这些就变得不必要了,因为您可以立即致电strtol() 并检查*endptr_n,但如果在线上可能还有其他内容是如何做到的,例如如果你想接受一个整数后跟任何非数字,但不是浮点数后跟相同的东西,你可以删除 if ( *endptr_d == '\0' || *endptr_d == '\n' ) 逻辑。

编辑:更新代码以显示检查到*endptr

【讨论】:

    【解决方案8】:

    如果您将数字表示为字符串(当您使用 fgets 时),您可以通过它运行 for 循环并将每个字符与 '.' 进行比较。

    【讨论】:

      【解决方案9】:

      我可以看到的另一个选项如下:

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      int main()
      {
      
       char mystring[31];
       scanf("%30s[0-9]\n", mystring);
       int mynumber = atoi(mystring);
       printf("here is your integer: %d", mynumber);
       getchar();
       getchar();
       return 0;
      
      
      
      
      
      
      
      
      
      
      
       }
      

      【讨论】:

      • 这不起作用。一个条目,例如12.34 报告已输入整数值 12,但 OP 希望将此类输入视为无效。您可以通过查看strtol() 找到解决方案。另请注意,这里不需要#include &lt;string.h&gt;
      • 这仍然不起作用。你试过这个代码吗?这里有一些重要的问题:s 不是 scanset 指令的一部分,所以格式字符串应该是"%30[0-9]"scanf() 格式字符串中不应(几乎)使用尾随空格字符;它们会对交互式输入造成严重破坏,并且不会像大多数人认为的那样做。事实上,对于"%30s[0-9]\n",只有%30s 匹配。即使进行了更正,12.34 的输入仍然被错误地视为 12 的输入。
      猜你喜欢
      • 2015-11-24
      • 1970-01-01
      • 1970-01-01
      • 2016-10-28
      • 1970-01-01
      • 1970-01-01
      • 2022-01-04
      • 2021-10-17
      • 1970-01-01
      相关资源
      最近更新 更多