我想知道在C语言中是否有将字符串转换为整数的替代方法。
我经常在我的代码中使用以下模式。
char s[] = "45";
int num = atoi(s);
那么,是否有更好或是另一种方法呢?
我想知道在C语言中是否有将字符串转换为整数的替代方法。
我经常在我的代码中使用以下模式。
char s[] = "45";
int num = atoi(s);
那么,是否有更好或是另一种方法呢?
我认为更好的选择是使用strtol
。此外,我还喜欢strtonum
,如果你有它,可以使用它(但请记住它不可移植):
long long
strtonum(const char *nptr, long long minval, long long maxval,
const char **errstr);
你可能还会对C99标准中的 strtoumax
和 strtoimax
函数感兴趣。例如,你可以这样说:
uintmax_t num = strtoumax(s, NULL, 10);
if (num == UINTMAX_MAX && errno == ERANGE)
/* Could not convert. */
无论如何,远离atoi
函数:
调用atoi(str)相当于:
(int) strtol(str, (char **)NULL, 10)
除此之外,错误处理可能会有所不同。 如果无法表示该值,则行为未定义。
#<stdlib.h>
中声明。但是,您可以使用标准的 strtoumax
替代方案。 - cnicutar基于C89的鲁棒strtol
解决方案
该方案具有以下特点:
atoi
系列相比)#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
typedef enum {
STR2INT_SUCCESS,
STR2INT_OVERFLOW,
STR2INT_UNDERFLOW,
STR2INT_INCONVERTIBLE
} str2int_errno;
/* Convert string s to int out.
*
* @param[out] out The converted int. Cannot be NULL.
*
* @param[in] s Input string to be converted.
*
* The format is the same as strtol,
* except that the following are inconvertible:
*
* - empty string
* - leading whitespace
* - any trailing characters that are not part of the number
*
* Cannot be NULL.
*
* @param[in] base Base to interpret string in. Same range as strtol (2 to 36).
*
* @return Indicates if the operation succeeded, or why it failed.
*/
str2int_errno str2int(int *out, char *s, int base) {
char *end;
if (s[0] == '\0' || isspace(s[0]))
return STR2INT_INCONVERTIBLE;
errno = 0;
long l = strtol(s, &end, base);
/* Both checks are needed because INT_MAX == LONG_MAX is possible. */
if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX))
return STR2INT_OVERFLOW;
if (l < INT_MIN || (errno == ERANGE && l == LONG_MIN))
return STR2INT_UNDERFLOW;
if (*end != '\0')
return STR2INT_INCONVERTIBLE;
*out = l;
return STR2INT_SUCCESS;
}
int main(void) {
int i;
/* Lazy to calculate this size properly. */
char s[256];
/* Simple case. */
assert(str2int(&i, "11", 10) == STR2INT_SUCCESS);
assert(i == 11);
/* Negative number . */
assert(str2int(&i, "-11", 10) == STR2INT_SUCCESS);
assert(i == -11);
/* Different base. */
assert(str2int(&i, "11", 16) == STR2INT_SUCCESS);
assert(i == 17);
/* 0 */
assert(str2int(&i, "0", 10) == STR2INT_SUCCESS);
assert(i == 0);
/* INT_MAX. */
sprintf(s, "%d", INT_MAX);
assert(str2int(&i, s, 10) == STR2INT_SUCCESS);
assert(i == INT_MAX);
/* INT_MIN. */
sprintf(s, "%d", INT_MIN);
assert(str2int(&i, s, 10) == STR2INT_SUCCESS);
assert(i == INT_MIN);
/* Leading and trailing space. */
assert(str2int(&i, " 1", 10) == STR2INT_INCONVERTIBLE);
assert(str2int(&i, "1 ", 10) == STR2INT_INCONVERTIBLE);
/* Trash characters. */
assert(str2int(&i, "a10", 10) == STR2INT_INCONVERTIBLE);
assert(str2int(&i, "10a", 10) == STR2INT_INCONVERTIBLE);
/* int overflow.
*
* `if` needed to avoid undefined behaviour
* on `INT_MAX + 1` if INT_MAX == LONG_MAX.
*/
if (INT_MAX < LONG_MAX) {
sprintf(s, "%ld", (long int)INT_MAX + 1L);
assert(str2int(&i, s, 10) == STR2INT_OVERFLOW);
}
/* int underflow */
if (LONG_MIN < INT_MIN) {
sprintf(s, "%ld", (long int)INT_MIN - 1L);
assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW);
}
/* long overflow */
sprintf(s, "%ld0", LONG_MAX);
assert(str2int(&i, s, 10) == STR2INT_OVERFLOW);
/* long underflow */
sprintf(s, "%ld0", LONG_MIN);
assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW);
return EXIT_SUCCESS;
}
str2int()
。苛刻:使用 isspace((unsigned char) s[0])
。 - chux - Reinstate Monica(unsigned char)
强制转换会有所不同吗? - Ciro Santilli OurBigBook.coml > INT_MAX
和l < INT_MIN
是无意义的整数比较,因为任何一个结果都是永远为假。如果我将它们改为l >= INT_MAX
和l <= INT_MIN
以消除警告,会发生什么?在ARM C中,long和int是32位有符号ARM C和C++中的基本数据类型。 - ecleif (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;
改为 if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}
,则可以让调用代码在 int
超出范围时使用 errno
。对于 if (l < INT_MIN...
同理。 - chux - Reinstate Monica*out
比像strtol()
那样设置它要少用。然而,这是一个设计批评,而不是这个实现的问题。 - chux - Reinstate Monicaato...
组中的函数。这些函数已经损坏并且几乎无用。一个更好的解决方案是使用 sscanf
,但它也不完美。strto...
组中的函数。在您的特定情况下,应该使用 strtol
函数。sscanf
试图将一个超出其类型范围的数字进行转换(例如 sscanf("999999999999999999999", "%d", &n)
),则实际上它具有未定义的行为。 - Keith Thompsonatoi
没有提供有意义的成功/失败反馈,并且在溢出时具有未定义的行为。sscanf
提供了某种程度的成功/失败反馈(返回值使其“适度更好”),但仍然在溢出时具有未定义的行为。只有strtol
才是可行的解决方案。 - AnT stands with Russiasscanf
可能存在的致命问题。(虽然我承认有时会使用atoi
,通常是为了那些我不指望源代码存活超过10分钟的程序。) - Keith Thompsonatoi()
函数来练习:int my_getnbr(char *str)
{
int result;
int puiss;
result = 0;
puiss = 1;
while (('-' == (*str)) || ((*str) == '+'))
{
if (*str == '-')
puiss = puiss * -1;
str++;
}
while ((*str >= '0') && (*str <= '9'))
{
result = (result * 10) + ((*str) - '0');
str++;
}
return (result * puiss);
}
code
((*str) - '0') code
- user618677"----1"
。my_getnbr("-2147483648")
。int atoi(const char* str){
int num = 0;
int i = 0;
bool isNegetive = false;
if(str[i] == '-'){
isNegetive = true;
i++;
}
while (str[i] && (str[i] >= '0' && str[i] <= '9')){
num = num * 10 + (str[i] - '0');
i++;
}
if(isNegetive) num = -1 * num;
return num;
}
如前所述,在任何C程序中都不应使用atoi
函数族,因为它们没有任何错误处理。
strtol
函数族是完全等效的,但具有扩展功能:它具有错误处理,还支持十六进制或二进制等其他进制。因此,正确的答案是:使用strtol
函数族。
如果你出于某些原因坚持手动编写此函数,应尝试在除可选符号和数字之外存在其他符号的情况下执行类似strtol
的操作。例如,我们经常需要转换作为字符串一部分的数字。
带有错误处理支持的简单版本可能看起来像下面的示例。此代码仅适用于十进制基数10的数字,但在指向第一个无效字符(如果有)的可选指针设置方面与strtol
表现相似。还请注意,此代码不处理溢出。
#include <ctype.h>
long my_strtol (char* restrict src, char** endptr)
{
long result=0;
long sign=1;
if(endptr != NULL)
{
/* if input is ok and endptr is provided,
it will point at the beginning of the string */
*endptr = src;
}
if(*src=='-')
{
sign = -1;
src++;
}
for(; *src!='\0'; src++)
{
if(!isdigit(*src)) // error handling
{
if(endptr != NULL)
{
*endptr = src;
}
break;
}
result = result*10 + *src - '0';
}
return result * sign;
}
long
类型,最大值为2147483647
,即10个数字。atoi
更糟糕。 - Lundin我想分享一下关于 unsigned long 的解决方案。
unsigned long ToUInt(char* str)
{
unsigned long mult = 1;
unsigned long re = 0;
int len = strlen(str);
for(int i = len -1 ; i >= 0 ; i--)
{
re = re + ((int)str[i] -48)*mult;
mult = mult*10;
}
return re;
}
const char *
。 - Roland Illig'0'
即可。 - Toby Speight好的,我也遇到了同样的问题。我想出了这个解决方案,对我来说效果最好。我尝试过使用atoi(),但效果不佳。所以这是我的解决方案:
void splitInput(int arr[], int sizeArr, char num[])
{
for(int i = 0; i < sizeArr; i++)
// We are subtracting 48 because the numbers in ASCII starts at 48.
arr[i] = (int)num[i] - 48;
}
你总是可以自己动手!
#include <stdio.h>
#include <string.h>
#include <math.h>
int my_atoi(const char* snum)
{
int idx, strIdx = 0, accum = 0, numIsNeg = 0;
const unsigned int NUMLEN = (int)strlen(snum);
/* Check if negative number and flag it. */
if(snum[0] == 0x2d)
numIsNeg = 1;
for(idx = NUMLEN - 1; idx >= 0; idx--)
{
/* Only process numbers from 0 through 9. */
if(snum[strIdx] >= 0x30 && snum[strIdx] <= 0x39)
accum += (snum[strIdx] - 0x30) * pow(10, idx);
strIdx++;
}
/* Check flag to see if originally passed -ve number and convert result if so. */
if(!numIsNeg)
return accum;
else
return accum * -1;
}
int main()
{
/* Tests... */
printf("Returned number is: %d\n", my_atoi("34574"));
printf("Returned number is: %d\n", my_atoi("-23"));
return 0;
}
这将实现你想要的功能,而不会混乱。
strto...
函数家族。它们具有可移植性并且更好。 - chad0x2d,0x30
而不是'-','0'
很奇怪。不允许使用'+'
符号。为什么在(int)strlen(snum)
中进行(int)
转换?如果输入是""
则会出现未定义行为。由于int
溢出而导致accum += (snum[strIdx] - 0x30) * pow(10, idx);
时结果为INT_MIN
时也会出现未定义行为。 - chux - Reinstate Monica//I think this way we could go :
int my_atoi(const char* snum)
{
int nInt(0);
int index(0);
while(snum[index])
{
if(!nInt)
nInt= ( (int) snum[index]) - 48;
else
{
nInt = (nInt *= 10) + ((int) snum[index] - 48);
}
index++;
}
return(nInt);
}
int main()
{
printf("Returned number is: %d\n", my_atoi("676987"));
return 0;
}
nInt = (nInt *= 10) + ((int) snum[index] - 48);
而不是nInt = nInt*10 + snum[index] - '0';
? if(!nInt)
不需要。 - chux - Reinstate Monica