下面的程序将字符串转换为long,但根据我的理解,它也会返回错误.我依赖的事实是,如果strtol将字符串成功转换为long,那么第二个参数strtol应该等于NULL.当我用55运行以下应用程序时,我收到以下消息.
./convertToLong 55
Could not convert 55 to long and leftover string is: 55 as long is 55
Run Code Online (Sandbox Code Playgroud)
如何成功检测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;
}
Run Code Online (Sandbox Code Playgroud)
Jon*_*ler 51
请注意,以下划线开头的名称是为实现保留的; 最好避免在代码中使用这些名称.因此,_val应该是公正的val.
strtol()当你第一次遇到它时,错误处理及其亲属的完整规范是复杂的,非常复杂.你正在做的一件事是使用一个函数来调用strtol(); 在代码中使用'raw'可能不正确.
由于问题用C和C++标记,我将引用C2011标准; 您可以自己在C++标准中找到适当的措辞.
ISO/IEC 9899:2011§7.22.1.4的
strtol,strtoll,strtoul和strtoull功能
long int strtol(const char * restrict nptr, char ** restrict endptr, int base);2[...]首先,它们将输入字符串分解为三个部分:一个初始的,可能是空的白色空格字符序列(由isspace函数指定),一个类似于某个基数所表示的整数的主题序列by base的值,以及一个或多个无法识别的字符的最终字符串,包括输入字符串的终止空字符.[...]
7如果主题序列为空或者没有预期的形式,则不进行转换; 如果不是空指针,则值
nptr存储在指向的对象中.endptrendptr返回
8的
strtol,strtoll,strtoul,和strtoull函数返回转换后的值,如果有的话.如果无法执行转换,则返回零.如果正确的值超出可表示值的范围,则返回LONG_MIN,LONG_MAX,LLONG_MIN,LLONG_MAX,ULONG_MAX或ULLONG_MAX(根据值的返回类型和符号,如果有),并且宏ERANGE的值为存储在errno.
请记住,没有标准C库函数设置errno为0.因此,为了可靠,您必须errno在调用之前将其设置为零strtol().
所以,你的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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,出错时,返回0或LONG_MIN或LONG_MAX,具体取决于strtol()返回的内容.如果您的调用代码需要知道转换是否成功,则需要一个不同的功能接口 - 见下文.另外,请注意应该打印错误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;
}
Run Code Online (Sandbox Code Playgroud)
您可以使用如下:
if (parseLong(str, &value))
…conversion successful…
else
…handle error…
Run Code Online (Sandbox Code Playgroud)
如果你需要区分'尾随垃圾','无效数字字符串','值太大'和'值太小'(和'无错误'),你将使用整数或enum代替布尔返回代码.如果要允许尾随空格而不允许其他字符,或者如果您不想允许任何前导空格,则在该函数中还有更多工作要做.代码允许八进制,十进制和十六进制; 如果你想要严格的十进制,你需要在调用中将0更改为10 strtol().
如果您的函数要伪装成标准库的一部分,则它们不应设置errno为0永久性,因此您需要包装代码以保留errno:
int saved = errno; // At the start, before errno = 0;
…rest of function…
if (errno == 0) // Before the return
errno = saved;
Run Code Online (Sandbox Code Playgroud)
chr*_*ris 19
你快到了.temp本身不会为null,但如果整个字符串被转换,它将指向一个空字符,因此您需要取消引用它:
if (*temp != '\0')
Run Code Online (Sandbox Code Playgroud)
如何成功检测 strtol 的错误?
static long parseLong(const char * str) {
int base = 0;
char *endptr;
errno = 0;
long val = strtol(str, &endptr, base);
Run Code Online (Sandbox Code Playgroud)
标准 C 库指定/支持的 3 个测试:
有转换完成吗?
if (str == endptr) puts("No conversion.");
Run Code Online (Sandbox Code Playgroud)
在范围内?
// Best to set errno = 0 before the strtol() call.
else if (errno == ERANGE) puts("Input out of long range.");
Run Code Online (Sandbox Code Playgroud)
拖尾垃圾?
else if (*endptr) puts("Extra junk after the numeric text.");
Run Code Online (Sandbox Code Playgroud)
成功
else printf("Success %ld\n", val);
Run Code Online (Sandbox Code Playgroud)
输入类似str == NULL或非base0、[2 到 36] 是未定义行为。各种实现(C 库的扩展)通过errno. 我们可以添加第四个测试。
else if (errno) puts("Some implementation error found.");
Run Code Online (Sandbox Code Playgroud)
或者结合测试errno == ERANGE。
示例简洁代码还利用了常见的实现扩展。
long my_parseLong(const char *str, int base, bool *success) {
char *endptr = 0;
errno = 0;
long val = strtol(str, &endptr, base);
if (success) {
*success = endptr != str && errno == 0 && endptr && *endptr == '\0';
}
return val;
}
Run Code Online (Sandbox Code Playgroud)