写在前边: 我没有完全解决此题目, 个人认为题目存在严重漏洞, 此篇仅为讨论稿, 请慎重阅读.
1.问题背景
此题来自于LeetCode, 题目要求复述如下: 手动实现一个atoi函数, 传入一个字符串, 输出其所表示的整数, 规则如下:
- 丢弃最前端空格字符和后端的非数字字符, 从第一个数字字符开始连接其后接续的数字字符, 直到遇到多余的字符, 丢弃后续并返回之前的结果
- 执行符号检查, 执行溢出检查, 上溢返回int上限, 反之返回下限
2.难点分析
字符串转整数本来可以通过一些封装好的方法/函数来实现(例如java中的int Integer.parseInt(String s)方法等), 但此题要求手动实现, 应当是考察程序员对于特殊进制书写规范的处理, 常用的十六进制/十进制/八进制/二进制表示法分别为(0x???/???/0???/0b???). 我们尝试了使用java中的Integer类提供的转换方法, 发现其无法将字符串"0x666"转化为整型0x666, 这也是我们重新造轮子的目的之一–即原有的轮子无法满足一些特殊场景下的应用.
3.代码实现
public class Problem0008_ATOI {
enum IntegerType{
BIN(0b10), OCT(010), DEC(10), HEX(0x10);
private int value;
private IntegerType(int value){this.value=value;}
public int value(){return value;}
public IntegerType valueof(int i) throws EnumConstantNotPresentException {
if(i==0b10) return BIN;
else if(i==010) return OCT;
else if(i==10) return DEC;
else if(i==0x10) return HEX;
else throw new EnumConstantNotPresentException(IntegerType.class,"不包含"+i+"进制体系");
}
}
public static int myAtoi(String str){
IntegerType type=IntegerType.DEC; //无标识时默认为十进制
boolean isTypeKnown=false; //未知进制的时候检查进制
boolean isNegative=false; //无符号时默认为正数
boolean isSymbolKnown=false; //不知道是啥符号的时候才执行符号检查
int res=0;
for(int i=0; i<str.length(); i++){
char c=str.charAt(i);
if(!isSymbolKnown && isSymbolic(c)) { isNegative=(c=='-'); isSymbolKnown=true; continue; } //执行符号检查
else if(!isTypeKnown && c=='0'){ //执行进制检查
char m=str.charAt(i+1);
if(m=='b') {type=IntegerType.BIN;isTypeKnown=true;i++;} //二进制标识符注意多推后一位
else if(m=='x') {type=IntegerType.HEX;isTypeKnown=true;i++;} //十六进制同理
else if(isNumeric(IntegerType.OCT,m)) {type=IntegerType.OCT;isTypeKnown=true;}// oct type
else return 0;// zero in all carry rules
}else if(isNumeric(IntegerType.DEC,c)){
isTypeKnown=true;// dec type
}
if(isNumeric(type,c)){
try{
int k=getNumber(c);
int g=type.value();
if(Integer.MAX_VALUE/g<res) return Integer.MAX_VALUE;
else if(Integer.MAX_VALUE/g==res && k>= Integer.MAX_VALUE%g) return Integer.MAX_VALUE;
if(Integer.MIN_VALUE/g>res) return Integer.MIN_VALUE;
else if(Integer.MIN_VALUE/g==res && k<= Integer.MIN_VALUE%g-g) return Integer.MIN_VALUE;
res*=type.value();
res=res+(isNegative?-k:k);
}
catch(ParseException e){System.exit(-1);}//此处出现bug说明虚拟机底层数据定义有问题 需要重新配置java
}else if(c!=' '){break;}
if(isNegative&&res>0) return Integer.MIN_VALUE;
if(!isNegative&&res<0) return Integer.MAX_VALUE;
}
return res;
}
public static boolean isNumeric(IntegerType tp, char c){
switch(tp){
case BIN: return c=='0'||c=='1';
case OCT: return c>='0'&&c<='7';
case DEC: return c>='0'&&c<='9';
case HEX: return (c>='0'&&c<='9') || (c>='a'&&c<='f') || (c>='A'&&c<='F');
default: return false;
}
}
private static int getNumber(char c) throws ParseException {
switch(c){
case '0': return 0x00; case '1': return 0x01; case '2': return 0x02; case '3': return 0x03;
case '4': return 0x04; case '5': return 0x05; case '6': return 0x06; case '7': return 0x07;
case '8': return 0x08; case '9': return 0x09; case 'a': return 0x0a; case 'A': return 0x0A;
case 'b': return 0x0b; case 'B': return 0x0B; case 'c': return 0x0c; case 'C': return 0x0C;
case 'd': return 0x0d; case 'D': return 0x0D; case 'e': return 0x0e; case 'E': return 0x0E;
case 'f': return 0x0f; case 'F': return 0x0F; default: throw new ParseException(String.valueOf(c),0);
}
}
public static boolean isSymbolic(char c){return c=='-'||c=='+';}
public static void main(String[] args){//本段代码用于个人测试
System.out.println(myAtoi("-0xabsorption"));
System.out.println(myAtoi("0xfFfFfffffff"));
System.out.println(myAtoi(" -0b11 loveWS"));
System.out.println(myAtoi("amazing t35t!"));
System.out.println(myAtoi("123dec HaHaHa"));
System.out.println(myAtoi("-91283472332"));
System.out.println(myAtoi("10 01 22 34 5"));
}
}
4.结果讨论
此段代码提交LeetCode并未通过, 原因是对于一个八进制输入形式: " 0000000000012345678", 其给出了01234567的输出(对应于十进制的342391), LeetCode参考答案为12345678, 目前并不能理解此参考答案的含义, 万望上传测试用例的仁兄给出合理解释.
事实上, 此输入颇具争议, 以0开头直接接续数字的表示方式为八进制形式, 而字符’8’在满八进位的系统中, 是非法的数字字符, 在本人的实现中, 被函数boolean isNumeric(IntegerType tp, char c)判定为非数字字符而丢弃(输入中的tp参数的值为IntegerType.OCT), 而本题中并未定义“数字字符”, 此处只能按照常识进行判定, 因此给出了与题目参考答案相悖的结果.