Correct usage of strtol
下面的程序将字符串转换为long,但根据我的理解,它还会返回错误。 我所依赖的事实是,如果
1 2 | ./convertToLong 55 Could not convert 55 to long and leftover string is: 55 as long is 55 |
如何成功检测strtol中的错误? 在我的应用程序中,零是有效值。
码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include <stdio.h> #include <stdlib.h> static long parseLong(const char * str); int main(int argc, char ** argv) { printf("%s as long is %ld ", 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; } |
请注意,以下划线开头的名称是为实现保留的;最好避免在代码中使用此类名称。因此,
第一次运行
由于该问题同时带有C和C ++标签,因此我将引用C2011标准。您可以在C ++标准中找到适合自己的措辞。
ISO/IEC 9899:2011 §7.22.1.4 The
strtol ,strtoll ,strtoul andstrtoull functions
long int strtol(const char * restrict nptr, char ** restrict endptr, int base); ?2 [...] First,
they decompose the input string into three parts: an initial, possibly empty, sequence of
white-space characters (as specified by the isspace function), a subject sequence
resembling an integer represented in some radix determined by the value of base, and a
final string of one or more unrecognized characters, including the terminating null
character of the input string. [...]?7 If the subject sequence is empty or does not have the expected form, no conversion is
performed; the value ofnptr is stored in the object pointed to byendptr , provided
thatendptr is not a null pointer.Returns
?8 The
strtol ,strtoll ,strtoul , andstrtoull functions return the converted
value, if any. If no conversion could be performed, zero is returned. If the correct value
is outside the range of representable values, LONG_MIN, LONG_MAX, LLONG_MIN,
LLONG_MAX, ULONG_MAX, or ULLONG_MAX is returned (according to the return type
and sign of the value, if any), and the value of the macro ERANGE is stored inerrno .
请记住,没有标准的C库函数曾经将
因此,您的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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' ", str, temp); // cerr <<"Could not convert '" << str <<"' to long and leftover string is '" // << temp <<"' "; return val; } |
请注意,发生错误时,这将返回0或LONG_MIN或LONG_MAX,这取决于返回的
现在,在库代码中,您可能不想打印任何内容,并且您的调用代码可能想知道转换是否成功,因此您也可以修改接口。在这种情况下,您可能需要修改函数,以便它返回成功/失败指示:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; } |
您可以这样使用:
1 2 3 4 | if (parseLong(str, &value)) …conversion successful… else …handle error… |
如果需要区分"跟踪垃圾","无效数字字符串","值太大"和"值太小"(以及"无错误"),则可以使用整数或
如果您的函数要伪装成标准库的一部分,则不应将
1 2 3 4 5 6 | int saved = errno; // At the start, before errno = 0; …rest of function… if (errno == 0) // Before the return errno = saved; |
你快到了。
1 | if (*temp != '\0') |
您缺少间接级别。您要检查字符是否为终止符
1 | if (*temp != '\0') |
顺便说一句,这不是错误检查的好方法。通过将输出指针与字符串结尾进行比较,无法完成
你应该检查一下
1 | *temp != '\0' |
您还应该能够根据以下要求在调用strotol后检查errno的值:
1 2 3 4 5 6 7 | 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. |