C 语言中整数与字符串的相互转换是在平常用着比较多的。我们都知道用atoi函数能将字符串转换成整数。atoi看起来很容易写,但是你真的考虑过具体细节、是否符合要求呢?
atoi简易版本,不考虑细节问题
int my_atoi(const char *str)
{
if(NULL == str)
return 0;
int ret = 0;
while(0 != *str)
{
ret = ret * 10 + *str - '0';
str++;
}
return ret;
}
从函数来看,就是遍历字符串,每遇到一个数字就加上原来的值乘以10。程序看去,没有什么毛病,例如:“1234”,都能正确的输出。但是如果要处理下面这种情况呢?
"-1234"
"+1234"
" "
"12345678910"
""
"123aab"
可以试一下,会发现并不是想象中的那样?
atoi考虑细节版本
int String_atoi(char *str)
{
char flag = '+';//指示结果是否带符号
long res = 0;
while (' ' == (*str)) //跳过开始的空格
str++;
if(*str=='-')//字符串带负号
{
str++;
flag = '-';//将标志设为负号
}
if(*str=='+')//字符串带正号
{
str++;//指向下一个字符
flag = '+';//将标志设为正号
}
//逐个字符转换,并累加到结果res
while(*str >= 48 && *str < 57)//如果是数字才进行转换,数字0~9的ASCII码:48~57
{
res = 10 * res + *str - 48;//字符'0'的ASCII码为48
str++;
}
if(flag == '-')//处理是负数的情况
{
res = -res;
}
if(flag == '+')//处理是正数的情况
{
res = +res;
}
return (int)res;
}
看上面的代码你看你注意到了,实现atoi需要考虑这些方面:
1.输入正负号
2.开头有空格
3.空字符串
...
咱再看看函数源码是怎么考虑细节问题的
int atoi(const char *nptr)
{
return (int)atol(nptr);
}
long atol(const char *nptr)
{
int c; /* 当前要转换的字符(一个一个字符转换成数字) */
long total; /* 当前转换结果 */
int sign; /* 标志转换结果是否带负号*/
/*跳过空格,空格不进行转换*/
while ( isspace((int)(unsigned char)*nptr) )
++nptr;
c = (int)(unsigned char)*nptr++;//获取一个字符准备转换
sign = c; /*保存符号标示*/
if (c == '-' || c == '+')
c = (int)(unsigned char)*nptr++; /*跳过'+'、'-'号,不进行转换*/
total = 0;//设置转换结果为0
while (isdigit(c)) {//如果字符是数字
total = 10 * total + (c - '0'); /* 根据ASCII码将字符转换为对应的数字,并且乘10累积到结果 */
c = (int)(unsigned char)*nptr++; /* 取下一个字符 */
}
//根据符号指示返回是否带负号的结果
if (sign == '-')
return -total;
else
return total;
}
现在来做一下对比,验证是否真的是上面说的那样:
#include <stdio.h>
#include <stdlib.h> //atoi
int String_atoi(char *str);//函数声明
int my_atoi(const char *str);
int main(int argc, char const *argv[])
{
printf("\"String_atoi\"字符串转换为数字:%d\n",String_atoi("1234"));
printf("\"String_atoi\"字符串转换为数字:%d\n",String_atoi("-1234"));
printf("\"String_atoi\"字符串转换为数字:%d\n",String_atoi("+1234"));
printf("\"String_atoi\"字符串转换为数字:%d\n",String_atoi(" "));
printf("\"String_atoi\"字符串转换为数字:%d\n",String_atoi("12345678910"));
printf("\"String_atoi\"字符串转换为数字:%d\n",String_atoi(""));
printf("\"String_atoi\"字符串转换为数字:%d\n",String_atoi("123aab"));
printf("================================\n");
printf("\"atoi\"字符串转换为数字:%d\n",atoi("1234"));
printf("\"atoi\"字符串转换为数字:%d\n",atoi("-1234"));
printf("\"atoi\"字符串转换为数字:%d\n",atoi("+1234"));
printf("\"atoi\"字符串转换为数字:%d\n",atoi(" "));
printf("\"atoi\"字符串转换为数字:%d\n",atoi("12345678910"));
printf("\"atoi\"字符串转换为数字:%d\n",atoi(""));
printf("\"atoi\"字符串转换为数字:%d\n",atoi("123aab"));
printf("================================\n");
printf("\"my_atoi\"字符串转换为数字:%d\n",my_atoi("1234"));
printf("\"my_atoi\"字符串转换为数字:%d\n",my_atoi("-1234"));
printf("\"my_atoi\"字符串转换为数字:%d\n",my_atoi("+1234"));
printf("\"my_atoi\"字符串转换为数字:%d\n",my_atoi(" "));
printf("\"my_atoi\"字符串转换为数字:%d\n",my_atoi("12345678910"));
printf("\"my_atoi\"字符串转换为数字:%d\n",my_atoi(""));
printf("\"my_atoi\"字符串转换为数字:%d\n",my_atoi("123aab"));
return 0;
}
int String_atoi(char *str)
{
char flag = '+';//指示结果是否带符号
long res = 0;
while (' ' == (*str)) //跳过开始的空格
str++;
if(*str=='-')//字符串带负号
{
str++;
flag = '-';//将标志设为负号
}
if(*str=='+')//字符串带正号
{
str++;//指向下一个字符
flag = '+';//将标志设为正号
}
//逐个字符转换,并累加到结果res
while(*str >= 48 && *str < 57)//如果是数字才进行转换,数字0~9的ASCII码:48~57
{
res = 10 * res + *str - 48;//字符'0'的ASCII码为48
str++;
}
if(flag == '-')//处理是负数的情况
{
res = -res;
}
if(flag == '+')//处理是正数的情况
{
res = +res;
}
return (int)res;
}
int my_atoi(const char *str)
{
if(NULL == str)
return 0;
int ret = 0;
while(0 != *str)
{
ret = ret * 10 + *str - '0';
str++;
}
return ret;
}
输出结果:
上面三个函数进行对比,代码中设置的需要根据需求而定,例如如果认为空字符串或只有负号转换是非法的,那么前面的代码将不符合要求,再重新实现。但这些都不是重点,重点是我们在考虑实现atoi函数的时候,需要考虑多种异常场景。
扫二维码关注微信公众号,获取技术干货