这道题是在剑指 Offer 67. 把字符串转换成整数的基础上衍生出来的,它需要处理更多的复杂情况,我们来看一下吧!
编程实现将字符串转换为整型数; int my_atoi(const char *str);
-
遇到非数字字母,停止转换;
示例: str = “234.324” ; 返回值是234; -
首先出现空格的字符串可以转换,转换开始后,遇到空格停止转换;
示例: str =" 342 456"; 返回值是342; -
可以处理正负号;
示例: str = " +234.bad" ; 返回值是 234; str =" -342ab.234" ; 返回值是:-342; -
可以处理八进制,和十六进制;
示例: str = " +0xa1"; 返回值是 161; str = " 0123" ; 返回值是 83; -
可以进行纠错,如果不小心输入将0写成小写字符‘o’,大写字符‘O’;将1写成小写’l’,大写‘L’;我们应该进行自动纠错。
示例: str = “L23” ; 返回值是 123; str =“1o2” ; 返回值是:102; -
可以处理int 型溢出的情况;
示例; str = “567898765678987656789”; str = 2147483647; (INT_MAX 宏在 limits.h 头文件中)
str = "-234324665544325345325; str = - 2147483648; (INT_MIN 宏在limits.h 头文件中)
【解题思路:】
我们在剑指 Offer 67. 把字符串转换成整数这篇中对大部分情况的处理已经做了详细的介绍,这篇我们就只详细讲解如何处理八、十六进制,纠错问题。因为处理的情况过多,所以我们需要将函数进行封装,这样让代码更加的模块化。我们不对十六进制,八进制进行溢出判断,因为方法都是一样的,有兴趣的可以自己加上进行判断。 我们将封装为下面几个模块:
主函数模块
:进行空格处理,非字符处理,正负号处理,根据条件调用其他函数。纠错函数模块
:利用switch对字符进行纠错。处理十进制函数
:调用纠错函数,进行字符转为十进制,判断溢出。处理八进制
:如果第一位数字为0,则表示为八进制,调用纠错函数,进行字符转为八进制。处理十六进制
:如果第一位为0,第二位为x,表示为十六进制,调用纠错函数,进行字符转为十六进制。
【1.主函数模块】: 主函数模块中的条件处理我们在上一篇已经讲过,这里就不再叙述。
【2.纠错函数模块】: 每次纠正一个字符,所以需要多次调用进行纠错。利用switch(x)函数进行纠错,最后返回纠错后的字符,格式为:
switch(x)
{
case 'o': //错误的字符
case 'O':
s='0'; //纠正为正确的
break;
……
}
采用这样的格式可以进行多个字符的纠错。
【3.处理十进制函数模块】:
-
循环整个字符串,进行纠错。
-
isdigit()函数判断是否为十进制整数,是:
temp=x-‘0’; res=res*10+temp; // 进行转换,字符连接操作
if(res>INT_MAX/10)|| (res==INT_MAX/10 && TEMP>7) //进行溢出判断 -
不是直接退出,最后返回res转换后的字符。
【4.处理八进制函数模块】:
八进制的处理和十进制区别不大,改变判断,连接条件即可。
- 纠错,判断字符是否属于八进制,可以判断它是否在[0~7]范围内,也可以进行isdigit()函数,字符8,9的判断。
- res=res*8+temp;八进制的转换,就不是用10做乘法了。
【5.处理十六进制函数模块】:
十六进制会存在字符的转换,如a->10,那么我们就不能用函数isdigit来判断了,需要用isxdigit()判断是否为十六进制
;
其次十六进制存在大写A,小写a,故为了统一,我们首先将其全部转换为小写处理
。
最后字符的转换需要分两种情况
:
(1)数字:和八进制,十进制一样,将乘的数字改为16即可:
temp=x-‘0’; res=res*16+temp ; // 进行转换,字符连接操作
(2)字符:将a->10,b->11;a的ASCII码为97,b为98,所以我们可以:‘b’-‘a’+10=98-97+10=11进行转换,所以字符公式为:
temp=x-‘a’+10;res=res*16+temp
那么整个十六进制处理流程为:
- 纠错,isxdigit()判断是否为十六进制;
- 判断是否为数字,为书数字进行数字方式的转换。
- 不是数字,进行字符方式的转换,先转换为小写,再进行数字获取,连接。
这就是实现这个函数的模块,我们可以用一张图来描述解决这个问题的整体思路:
那我们给出代码:
//修改错误函数
char change(char x)
{
char s=x;
switch(x)
{
case 'o':
case 'O':
s='0';
break;
case 'l':
case 'L':
s='1';
break;
default:
s=x;
}
return s;
}
int dec(const char* str,int len,int res,int temp,int index,int flag)//处理十进制
{
for(int i=index;i<len;i++)
{
char x=change(str[i]);//进行纠错,后面都用字符x进行判断
if(!isdigit(x))//非数字字母,停止转换
{
break;
}
temp=x-'0';
if (res > INT_MAX / 10 || (res == INT_MAX/10 && temp > 7))
{
if(flag)
return INT_MIN;
else
return INT_MAX;
}
res=res*10+temp;
}
return res;
}
int hex(const char* str,int len,int res,int temp,int index)//处理十六进制
{
index++;
for(int i=index;i<len ;i++)
{
char x=change(str[i]);//进行纠错
if(isxdigit(x))//判断是否为16进制
{
if(isdigit(x))//数字
{
temp=x-'0';
res=res*16+temp;
}
else//字符
{
char s=tolower(x);//转换为小写,str[i]不会改变,x保存转换后的值
temp=s-'a'+10;//字符转换,f->15
res=res*16+temp;
}
}
else
break;
}
return res;
}
int oct(const char* str,int len,int res,int temp,int index)//处理八进制
{
//0123,从1开始处理,所以不用++
for(int i=index;i<len;i++)
{
char x=change(str[i]);//进行纠错
if(x>='0' && x<='7')
{
temp=x-'0';
res=res*8+temp;
}
else
break;
}
return res;
}
int my_atoi(const char *str)
{
int temp=0;
int res=0;
int len=(int)strlen(str);
bool flag=false;//标识负号
//处理空格
int index=0;//标识空格出现的位置
while(str[index]==' ')
index++;
if(str[index]=='-'||str[index]=='+')
{
if(str[index]=='-')
{
flag=true;//表示是负数
}
index++;
}
if(str[index]!='0')//不存在十六进制,八进制
{
res=dec(str,len,res,temp,index,flag);
}
else //处理十六进制,八进制
{
index++;//因为index位为0,现在要判断0后面的数值
if(str[index]=='x')//十六进制
{
res=hex(str,len,res,temp,index);
}
else//八进制
{
res=oct(str,len,res,temp,index);
}
}
if(flag)//为负数
{
res=0-res;
}
return res;
}
int main()
{
cout<<"遇到非数字字母,停止转换:"<<my_atoi("l34.324")<<endl;
cout<<"首先出现空格的字符串可以转换,转换开始后,遇到空格停止转换:"<<my_atoi(" 342 456")<<endl;
cout<<"处理正号:"<<my_atoi(" +204.bad")<<endl;
cout<<"处理负号:"<<my_atoi(" -342ab.234")<<endl;
cout<<"十六进制:"<<my_atoi(" +0xa1")<<endl;
cout<<"八进制:"<<my_atoi("0123")<<endl;
cout<<"纠正L,l错误:"<<my_atoi("L23")<<endl;
cout<<"纠正O,o错误:"<<my_atoi("1o2")<<endl;
cout<<"上溢出:"<<my_atoi("2147483647")<<endl;
cout<<"下溢出:"<<my_atoi("-2147483648")<<endl;
}
运行结果如下:
加油哦!💪。