请注意,以下划线开头的名称由实现保留;最好避免在您的代码中使用这些名称。因此,_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 函数所指定的),一个类似于在基数中表示的整数的主体序列,而这个基数由基数的值决定,并且具有一个或多个无法识别的字符的最终字符串,包括输入字符串的终止空字符。[...]
¶7 如果主体序列为空或没有预期的形式,则不执行转换;如果 endptr
不是 null 指针,则将 nptr
的值存储在所指向的对象中。
返回值
¶8 函数 strtol
、strtoll
、strtoul
和 strtoull
返回转换后的值(如果有)。如果无法执行任何转换,则返回零。如果正确的值超出了可表示值的范围,则返回 LONG_MIN、LONG_MAX、LLONG_MIN、LLONG_MAX、ULONG_MAX 或 ULLONG_MAX(根据值的返回类型和符号而定),并将宏 ERANGE 的值存储在 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);
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…
如果你需要区分‘尾随垃圾’、‘无效数值字符串’、‘值过大’和‘值过小’(以及‘无错误’),则应该使用整数或
枚举
而不是布尔型返回代码。如果允许尾随空格但不允许其他字符,或者不允许任何前导空格,则需要在函数中做更多的工作。此代码允许八进制、十进制和十六进制;如果你想要严格的十进制,则需要将调用
strtol()
的0更改为10。
如果你的函数要伪装成标准库的一部分,它们就不应该永久地将
errno
设置为
0
,所以你需要包装代码以保留
errno
。
int saved = errno; // At the start, before errno = 0;
…rest of function…
if (errno == 0) // Before the return
errno = saved;
strto*
函数的适当错误检查并不是通过检查输出指针来完成的。应该通过检查零返回值和设置errno
来完成。 - user529758std::stoi
?(你添加了C++标签) - BatchyXstd::stoi
完全做同样的事情。实际上,stoi
的原型与strtol
几乎相同,但在应该使用异常而不是错误返回值和全局错误变量hackery时使用异常。 - BatchyX