有没有办法检查字符串是否可以是C中的浮点数?

检查它是否可以是一个int很容易 – 只需检查每个数字是否在'0''9' 。 但浮动更难。 我找到了这个 ,但没有一个答案真的有效。 请考虑此代码段,基于顶部(已接受)答案:

 float f; int ret = sscanf("5.23.fkdj", "%f", &f); printf("%d", ret); 

1将被打印。


另一个答案建议使用strpbrk ,检查是否存在某些非法字符,但这不起作用,因为5fin7不合法,但inf会。


另一个答案建议检查strtod的输出。 但考虑一下:

 char *number = "5.53 garbanzo beans" char *foo; strtod(number, &foo); printf("%d", isspace(*foo) || *foo == '\0')); 

它会打印1 。 但我不想完全删除isspace调用,因为" 5.53 "应该是一个有效的数字。


是否有一种优秀,优雅,惯用的方式来做我想做的事情?

如果将它与%n (即读取的字符数)组合,第一个答案应该有效:

 int len; float ignore; char *str = "5.23.fkdj"; int ret = sscanf(str, "%f %n", &ignore, &len); printf("%d", ret==1 && !str[len]); 

如果字符串包含float未包含的字符,则!str[len]表达式将为false。 还要注意%f之后的空格以处理尾随空格。

演示

您可以检查 – 在使用strtod读取值后 – 其余部分仅由空格组成。 函数strspn可以在这里提供帮助,你甚至可以定义“你的个人空白区域”来考虑:

 int main() { char *number = "5.53 garbanzo beans"; char *foo; double d = strtod(number, &foo); if (foo == number) { printf("invalid number."); } else if (foo[strspn(foo, " \t\r\n")] != '\0') { printf("invalid (non-white-space) trailing characters."); } else { printf("valid number: %lf", d); } } 

有没有办法检查字符串是否可以是浮点数?

sscanf(...,"%f")方法的问题是溢出,即UB。 然而,它通常很好地处理。

而是使用float strtof(const char * restrict nptr, char ** restrict endptr);

 int float_test(const char *s) { char *ednptr; errno = 0; float f = strtof(s, &endptr); if (s == endptr) { return No_Conversion; } while (isspace((unsigned char) *endptr)) { // look past the number for junk endptr++; } if (*endptr) { return Extra_Junk_At_End; } // If desired // Special cases with with underflow not considered here. if (errno) { return errno; // likely under/overflow } return Success; } 

此代码非常基于dasblinkenlight的答案 。 我把它作为思考的食物。 它给出的一些答案可能不是你想要的。

 #include  #include  static void test_float(const char *str) { int len; float dummy = 0.0; if (sscanf(str, "%f %n", &dummy, &len) == 1 && len == (int)strlen(str)) printf("[%s] is valid (%.7g)\n", str, dummy); else printf("[%s] is not valid (%.7g)\n", str, dummy); } int main(void) { test_float("5.23.fkdj"); // Invalid test_float(" 255. "); // Valid test_float("255.123456"); // Valid test_float("255.12E456"); // Valid test_float(" .255 "); // Valid test_float(" Inf "); // Valid test_float(" Infinity "); // Valid test_float(" Nan "); // Valid test_float(" 255 "); // Valid test_float(" 0x1.23P-24 "); // Valid test_float(" 0x1.23 "); // Valid test_float(" 0x123 "); // Valid test_float("abc"); // Invalid test_float(""); // Invalid test_float(" "); // Invalid return 0; } 

使用GCC 7.1.0作为编译器在运行macOS Sierra 10.12.6的Mac上进行测试,得到输出:

 [5.23.fkdj] is not valid (5.23) [ 255. ] is valid (255) [255.123456] is valid (255.1235) [255.12E456] is valid (inf) [ .255 ] is valid (0.255) [ Inf ] is valid (inf) [ Infinity ] is valid (inf) [ Nan ] is valid (nan) [ 255 ] is valid (255) [ 0x1.23P-24 ] is valid (6.775372e-08) [ 0x1.23 ] is valid (1.136719) [ 0x123 ] is valid (291) [abc] is not valid (0) [] is not valid (0) [ ] is not valid (0) 

hex数可能特别成问题。 各种forms的无限和非数字也可能很麻烦。 并且带有指数( 255.12E456 )的一个示例溢出float并生成无穷大 – 这真的没问题吗?

这里提出的大多数问题都是定义性的 – 也就是说,你如何定义你想要接受的东西。 但请注意, strtod()将接受所有有效字符串(以及一些无效字符串,但其他测试会显示这些问题)。

显然,可以修改测试代码以使用包含字符串和所需结果的结构数组,这可以用于迭代显示的测试用例和您添加的任何额外内容。

strlen()结果的-Werror避免了编译警告(因为我使用-Werror编译时-Werror ) – 有comparison between signed and unsigned integer expressions [-Werror=sign-compare] 。 如果你的字符串足够长, strlen()的结果溢出了一个signed int ,你就会遇到其他问题,假装它们是有效的值。 OTOH,您可能想要在小数点后试验500位数 – 这是有效的。

此代码注释了对dasblinkenlight的回答的评论:

  • 领先的空白格式
  • 棘手的特殊情况
  • 大纲修复 – 现在在答案中采用。

这是由dasblinkenlight发布的代码片段的变体,由于strlen(str)可能代价高昂,因此稍微简单且效率更高:

 const char *str = "5.23.fkdj"; float ignore; char c; int ret = sscanf(str, "%f %c", &ignore, &c); printf("%d", ret == 1); 

说明:当且仅当转换了float时, sscanf()返回1 ,后跟可选的空格而没有其他字符。

也许这个? 不太好,但可以做这个工作。 在没有完成转换的情况下,在错误0上返回-1,在设置了转换数字标志的情

 #define INT_CONVERTED (1 << 0) #define FLOAT_CONVERTED (1 << 1) int ReadNumber(const char *str, double *db, int *in) { int result = (str == NULL || db == NULL || in == NULL) * -1; int len = 0; char *tmp; if (result != -1) { tmp = (char *)malloc(strlen(str) + 1); strcpy(tmp, str); for (int i = strlen(str) - 1; i >= 0; i--) { if (isspace(tmp[i])) { tmp[i] = 0; continue; } break; } if (strlen(tmp)) { if (sscanf(tmp, "%lf%n", db, &len) == 1 && strlen(tmp) == len) { result |= FLOAT_CONVERTED; } if (sscanf(tmp, "%d%n", in, &len) == 1 && strlen(tmp) == len) { result |= INT_CONVERTED; } } free(tmp); } return result; }