【Java基础】手动实现字符串转整数(atoi) 暨 一篇LeetCode题目讨论

4 篇文章 0 订阅
1 篇文章 0 订阅

写在前边: 我没有完全解决此题目, 个人认为题目存在严重漏洞, 此篇仅为讨论稿, 请慎重阅读.

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), 而本题中并未定义“数字字符”, 此处只能按照常识进行判定, 因此给出了与题目参考答案相悖的结果.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值