gcc 4.4.4 c89
将字符串转换为整数值更好。
我尝试了两种不同的方法atoi和sscanf。两者都按预期工作。
char digits[3] = "34";
int device_num = 0;
if(sscanf(digits, "%d", &device_num) == EOF) {
fprintf(stderr, "WARNING: Incorrect value for device\n");
return FALSE;
}
或使用atoi
device_num = atoi(digits);
我认为sscanf会更好,因为你可以检查错误。但是,atoi没有做任何检查。
答案 0 :(得分:104)
您有3个选择:
atoi
如果您在性能关键代码中使用它,这可能是最快的,但它没有错误报告。如果字符串不是以整数开头,则返回0.如果字符串在整数后面包含垃圾,它将转换初始部分并忽略其余部分。如果数字太大而不适合int
,则行为未指定。
sscanf
某些错误报告,您可以灵活地存储要存储的类型(char/short/int/long/long long/size_t/ptrdiff_t/intmax_t
的签名/未签名版本)。
返回值是成功的转换次数,因此如果字符串不以整数开头,则对"%d"
的扫描将返回0。您可以使用"%d%n"
来存储在另一个变量中读取的整数之后的第一个字符的索引,从而检查整个字符串是否已转换或之后是否有垃圾。但是,与atoi
类似,未指定整数溢出的行为。
strtol
和家人强大的错误报告,前提是您在拨打电话前将errno
设置为0。返回值在溢出时指定,errno
将被设置。您可以选择2到36之间的任意数字,或指定0作为基数,分别将前导0x
和0
自动解释为十六进制和八进制。要转换为的类型的选择是long/long long/intmax_t
的签名/无符号版本。
如果您需要较小的类型,您始终可以将结果存储在临时long
或unsigned long
变量中,并自行检查溢出。
由于这些函数采用指向指针参数的指针,因此您还可以获得指向转换后的整数后面的第一个字符的指针,这样您就可以判断整个字符串是整数还是解析字符串中的后续数据(如果需要)
就个人而言,我会建议strtol
家庭用于最目的。如果你正在做一些快速和肮脏的事情,atoi可能会满足你的需求。
顺便说一句,有时我发现我需要解析前导空格,符号等不被接受的数字。在这种情况下,很容易推出自己的循环,例如,
for (x=0; (unsigned)*s-'0'<10; s++)
x=10*x+(*s-'0');
或者你可以使用(为了健壮性):
if (isdigit(*s))
x=strtol(s, &s, 10);
else /* error */
答案 1 :(得分:10)
*scanf()
函数族返回转换的值的数量。所以你应该检查以确保sscanf()
在你的情况下返回1。 “输入失败”会返回EOF
,这意味着ssacnf()
永远不会返回EOF
。
对于sscanf()
,函数必须解析格式字符串,然后解码整数。 atoi()
没有那个开销。两者都存在超出范围的值导致未定义行为的问题。
您应该使用strtol()
或strtoul()
函数,它们可以提供更好的错误检测和检查功能。他们还会告诉您整个字符串是否被消耗。
如果您想要int
,可以随时使用strtol()
,然后检查返回的值,看它是否位于INT_MIN
和INT_MAX
之间。
答案 2 :(得分:4)
致@R ..我认为在errno
调用中检查strtol
错误检测是不够的。
long strtol (const char *String, char **EndPointer, int Base)
您还需要检查EndPointer
是否有错误。
答案 3 :(得分:2)
为了简洁而结合R ..和PickBoy答案
long strtol (const char *String, char **EndPointer, int Base)
// examples
strtol(s, NULL, 10);
strtol(s, &s, 10);
答案 4 :(得分:2)
如果不关心无效的字符串输入或范围问题,请使用最简单的:atoi()
否则,具有最佳错误/范围检测的方法既不是atoi()
也不是sscanf()
。
This good answer所有准备好的详细信息都表明缺少使用atoi()
进行错误检查以及使用sscanf()
进行某些错误检查。
strtol()
是将字符串转换为int
时最严格的功能。然而,这只是一个开始。下面是详细的示例,以显示正确的用法,以及accepted one之后的答案的原因。
// Over-simplified use
int strtoi(const char *nptr) {
int i = (int) strtol(nptr, (char **)NULL, 10);
return i;
}
这就像atoi()
一样,忽略了使用strtol()
的错误检测功能。
要完全使用strtol()
,需要考虑各种功能:
检测无转化:示例:"xyz"
,或""
或"--0"
?在这些情况下,endptr
将与nptr
匹配。
char *endptr;
int i = (int)strtol(nptr, &endptr, 10);
if (nptr == endptr) return FAIL_NO_CONVERT;
整个字符串应该转换还是仅转换为引导部分:"123xyz"
是否正常?
char *endptr;
int i = (int)strtol(nptr, &endptr, 10);
if (*endptr != '\0') return FAIL_EXTRA_JUNK;
检测值是否过大,结果无法表示为long
"999999999999999999999999999999"
。
errno = 0;
long L = strtol(nptr, &endptr, 10);
if (errno == ERANGE) return FAIL_OVERFLOW;
检测该值是否超出int
范围,但不是long
。如果int
和long
具有相同的范围,则不需要此测试。
long L = strtol(nptr, &endptr, 10);
if (L < INT_MIN || L > INT_MAX) return FAIL_INT_OVERFLOW;
某些实施超出了C标准,并设置了errno
,原因还有其他原因,例如errno to EINVAL in case no conversion was performed或EINVAL
The value of the Base parameter is not valid.。测试这些errno
值的最佳时间取决于实现。
将所有这些放在一起:(根据您的需要调整)
#include <errno.h>
#include <stdlib.h>
int strtoi(const char *nptr, int *error_code) {
char *endptr;
errno = 0;
long i = strtol(nptr, &endptr, 10);
#if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX
if (errno == ERANGE || i > INT_MAX || i < INT_MIN) {
errno = ERANGE;
i = i > 0 : INT_MAX : INT_MIN;
*error_code = FAIL_INT_OVERFLOW;
}
#else
if (errno == ERANGE) {
*error_code = FAIL_OVERFLOW;
}
#endif
else if (endptr == nptr) {
*error_code = FAIL_NO_CONVERT;
} else if (*endptr != '\0') {
*error_code = FAIL_EXTRA_JUNK;
} else if (errno) {
*error_code = FAIL_IMPLEMENTATION_REASON;
}
return (int) i;
}
注意:所有提到的功能都允许前导空格,可选的前导符号字符,并受 locale 更改的影响。更严格的转换需要额外的代码。
注意:非OP标题更改偏重。这个答案更适用于原始标题“将字符串转换为整数sscanf或atoi”
答案 5 :(得分:0)
如果用户输入34abc并将它们传递给atoi,它将返回34。 如果要验证输入的值,则必须迭代地在输入的字符串上使用isdigit