正确使用strtol

下面的程序将string转换为long,但基于我的理解,它也返回一个错误。 我依靠的事实是,如果strtol成功地将string转换为long,那么strtol的第二个参数应该等于NULL。 当我用55运行下面的应用程序时,我收到以下消息。

 ./convertToLong 55 Could not convert 55 to long and leftover string is: 55 as long is 55 

我怎样才能成功检测到strtol错误? 在我的应用程序中,零是一个有效的值。

码:

 #include <stdio.h> #include <stdlib.h> static long parseLong(const char * str); int main(int argc, char ** argv) { printf("%s as long is %ld\n", argv[1], parseLong(argv[1])); return 0; } static long parseLong(const char * str) { long _val = 0; char * temp; _val = strtol(str, &temp, 0); if(temp != '\0') printf("Could not convert %s to long and leftover string is: %s", str, temp); return _val; } 

你快到了 temp本身不会为空,但是如果整个string被转换,它将指向一个空字符,所以你需要解引用它:

 if (*temp != '\0') 

请注意,以下划线开头的名字是为实现保留的; 最好避免在你的代码中使用这样的名字。 因此, _val应该是val

strtol()及其亲属的error handling的完整规范是非常复杂的,当你第一次运行它的时候是非常复杂的。 你做的一件事情是绝对正确的是使用一个函数来调用strtol() ; 在代码中使用“原始”可能是不正确的。

由于问题是用C和C ++标记的,我将引用C2011标准; 你可以在自己的C ++标准中find合适的措词。

ISO / IEC 9899:2011§7.22.1.4strtol, strtollstrtoulstrtoull函数

long int strtol(const char * restrict nptr, char ** restrict endptr, int base);

首先,他们将inputstring分解为三部分:初始的,可能为空的空白字符序列(由isspace函数指定),主题序列类似于以某个基数表示的整数由base的值和一个或多个无法识别的字符的最后一个string,包括inputstring的终止空字符。 […]

¶7如果主题序列为空或者没有预期的forms,则不执行转换; 如果endptr不是空指针,则nptr的值存储在由endptr指向的对象中。

返回

¶8 strtolstrtollstrtoulstrtoull函数会返回转换的值(如果有的话)。 如果不能执行转换,则返回零。 如果正确值超出了可表示值的范围,则返回LONG_MIN,LONG_MAX,LLONG_MIN,LLONG_MAX,ULONG_MAX或ULLONG_MAX(根据返回types和值的符号(如果有)),并且macrosERANGE的值为存储在errno

请记住,没有标准C库函数将errno为0.因此,为了可靠,您必须在调用strtol()之前将errno为零。

所以,你的parseLong()函数可能看起来像这样:

 static long parseLong(const char *str) { errno = 0; char *temp; long val = strtol(str, &temp, 0); if (temp == str || *temp != '\0' || ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE)) fprintf(stderr, "Could not convert '%s' to long and leftover string is: '%s'\n", str, temp); // cerr << "Could not convert '" << str << "' to long and leftover string is '" // << temp << "'\n"; return val; } 

请注意,如果出现错误,则返回0或LONG_MIN或LONG_MAX,具体取决于返回的strtol() 。 如果你的调用代码需要知道转换是否成功,你需要一个不同的function接口 – 见下文。 此外,请注意错误应打印到stderr而不是stdout ,错误消息应以换行符终止\n ; 如果不是,他们不能保证及时出现。

现在,在库代码中,您可能不需要任何打印,并且您的调用代码可能想知道转换是否成功,因此您也可以修改该接口。 在这种情况下,您可能会修改该函数,以便返回成功/失败指示:

 bool parseLong(const char *str, long *val) { char *temp; bool rc = true; errno = 0; *val = strtol(str, &temp, 0); if (temp == str || *temp != '\0' || ((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE)) rc = false; return rc; } 

你可以像这样使用:

 if (parseLong(str, &value)) …conversion successful… else …handle error… 

如果您需要区分“尾随垃圾”,“无效数字串”,“值太大”和“值太小”(以及“无错误”),您可以使用整数或enum而不是布尔返回码。 如果你想允许尾随的空白而不是其他的字符,或者如果你不想允许任何前导的空格,那么在函数中有更多的工作要做。 代码允许八进制,十进制和hex; 如果你想严格十进制,你需要改变调用strtol()的0到10。

如果你的函数伪装成标准库的一部分,它们不应该将errno永久errno0 ,所以你需要包装代码来保存errno

 int saved = errno; // At the start, before errno = 0; …rest of function… if (errno == 0) // Before the return errno = saved; 

你错过了一个间接的程度。 你想检查字符是否是终止的NUL ,而不是指针是否为NULL

 if (*temp != '\0') 

顺便说一句,这不是错误检查的好方法。 strto*系列函数的错误检查方法不是通过比较输出指针和string结尾来完成的。 这应该通过检查一个零返回值并获取errno的返回值来完成。

你应该检查

 *temp != '\0' 

你应该也能够在调用strotol之后检查errno的值:

 RETURN VALUES The strtol(), strtoll(), strtoimax(), and strtoq() functions return the result of the conversion, unless the value would underflow or overflow. If no conver- sion could be performed, 0 is returned and the global variable errno is set to EINVAL (the last feature is not portable across all platforms). If an overflow or underflow occurs, errno is set to ERANGE and the function return value is clamped according to the following table.