请注意,以下划线开头的名称是为实现保留的;最好避免在代码中使用此类名称。因此,_val
应该是公正val
的。
当您第一次遇到错误处理strtol()
及其相关的完整规范时,它是复杂的,令人惊讶的复杂。您做的一件绝对正确的事情是使用函数来调用strtol()
;在代码中使用它“原始”可能是不正确的。
由于该问题同时使用 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 函数指定),一个类似于整数的主题序列,以某个确定的基数表示由 base 的值和一个或多个无法识别的字符组成的最终字符串,包括输入字符串的终止空字符。[...]
¶7 如果主题序列为空或不具有预期形式,则不进行转换;的值nptr
存储在 指向的对象中endptr
,前提endptr
是该对象不是空指针。
退货
¶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;
}
请注意,出错时,这将返回 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;
}
你可以像这样使用:
if (parseLong(str, &value))
…conversion successful…
else
…handle error…
如果您需要区分“尾随垃圾”、“无效数字字符串”、“值太大”和“值太小”(以及“无错误”),您可以使用整数或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;