在ANSI C中确定一个字符串是整数还是浮点数

9

使用仅限ANSI C,最好的方法是如何以公正的确定性确定C样式字符串是整数还是实数(即float/double)?

9个回答

34
不要使用atoi和atof函数,因为这些函数在失败时返回0。我上次检查的时候0是一个有效的整数和浮点数,所以它们无法确定类型。
应该使用strto{l,ul,ull,ll,d}函数,因为它们在失败时设置errno,并报告转换数据的结束位置。
strtoul:http://www.opengroup.org/onlinepubs/007908799/xsh/strtoul.html 此示例假设字符串包含要转换的单个值。
#include <errno.h>

char* to_convert = "some string";
char* p = to_convert;
errno = 0;
unsigned long val = strtoul(to_convert, &p, 10);
if (errno != 0)
    // conversion failed (EINVAL, ERANGE)
if (to_convert == p)
    // conversion failed (no characters consumed)
if (*p != 0)
    // conversion failed (trailing data)

感谢 Jonathan Leffler 指出我忘记首先将 errno 设置为 0。


抱歉,我知道这已经很旧了,但在这一行中,我们没有任何errno的信息? unsigned long val = strtoul(to_convert, &p, 10);正确的写法应该是: unsigned long errno = strtoul(to_convert, &p, 10); - vodkhang
@vodkhang 这是有效的代码,但根据您的编译器,您可能需要使用#include <errno.h>。 errno本质上是一个可以设置错误代码的全局变量。它可以实现为宏或“可修改的lvalue”。 - Ben Gartner
errno是一个宏,它扩展为可修改的lvalue。通常情况下,它会扩展为(*__errno_location())或类似的内容。 - R.. GitHub STOP HELPING ICE
为了避免任何麻烦,你可能需要添加#include <errno.h>才能使其正常工作。 - Dave Appleton

9
使用 sscanf,您可以确定字符串是浮点数、整数或其他类型,而无需像使用 atoi 和 atof 解决方案一样特殊处理 0。
以下是示例代码:
int i;
float f;
if(sscanf(str, "%d", &i) != 0) //It's an int.
  ...
if(sscanf(str "%f", &f) != 0)  //It's a float.
  ...

1
我非常确定你需要测试 sscanf 的返回值与 str 的 sizeof 进行比较,以确保整个字符串被转换,否则如果传递了 "1.374","%d" 将不会失败,它将返回 1。 - Patrick_O
真的。这也可以通过首先测试%f来解决,但这会导致“1.”的问题。就个人而言,我喜欢你提出的解决方案最好。 - Patrick
@Patrick_O 检查 sscanf 的返回值与字符串长度是行不通的,因为它返回成功转换的项目数,而不是用于转换的字符数。但 %n 格式在这里可能有帮助。 - Jens

3
我同意Patrick_O的观点,strto{l,ul,ull,ll,d}函数是最好的选择。但需要注意以下几点:
  1. 在调用函数之前将errno设置为零;没有任何函数会为您执行此操作。
  2. 链接到的Open Group页面(在我注意到Patrick也链接到它之前)指出,errno可能未设置。如果值超出范围,则将其设置为ERANGE;如果参数无效,则可能设置(但同样可能不设置)为EINVAL。
根据手头的工作,有时我会安排跳过返回的转换指针末尾的空格,并抱怨(拒绝),如果最后一个字符不是终止空字符'\0'。或者我可以懒散地让垃圾出现在末尾,或者我可以接受基于上下文的可选乘数,如'K'、'M'、'G'、'T'用于千字节、兆字节、千兆字节、太字节等,或者其他任何要求。

3

atoi和atof会在无法转换时返回0。


当然 - 你的回答肯定涵盖了那种情况。 - itsmatt
我已经更改了我的显示名称,以停止这种混淆! - Patrick_O

2

我想你可以逐个检查字符串,并检查其中是否有任何.字符。尽管这只是我脑海中想到的第一件事,但我相信还有其他更好的方法来更加确定。


还有逗号','字符。别忘了本地化。 - Arafangion

2
请使用strtol/strtoll(而不是atoi)检查整数。 请使用strtof/strtod(而不是atof)检查浮点数。
atoi和atof只转换字符串的初始部分,但不告诉您它们是否使用了所有字符串。strtol/strtod会告诉您是否在转换字符后还有额外的垃圾内容。
因此,在两种情况下,请记得传递非空的TAIL参数,并检查它是否指向字符串的末尾(即 **TAIL == 0)。还要检查返回值以防止下溢和上溢(请参阅man页面或ANSI标准以获取详细信息)。
还要注意,strtol/strtod会跳过初始空格,因此,如果您想将带有初始空格的字符串视为格式不正确,则还需要检查第一个字符。

1

这真的取决于你为什么首先提出这个问题。

如果你只想解析一个数字,而不知道它是浮点数还是整数,那么只需解析一个浮点数,它将正确地解析一个整数。

如果你实际上想知道类型,可能是为了分类,那么你应该考虑按照你认为最相关的顺序测试类型。比如尝试解析一个整数,如果不能,则尝试解析一个浮点数。(反过来会产生更多的浮点数...)


1

atoi和atof会转换数字,即使有尾随的非数字字符。然而,如果您使用strtol和strtod,它不仅会跳过前导空格和可选符号,而且会留下一个指向不在数字中的第一个字符的指针。然后,您可以检查其余部分是否为空格。


0

如果你不想使用像strtoul这样的新函数,你可以添加另一个strcmp语句来查看字符串是否为0。

例如:

if(atof(token) != NULL || strcmp(token, "0") == 0)

这意味着“0.0”(以及其他类似的变体)不是数字。 - Michael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接