源码学习字符串转换成整数Intenger.parseInt(String s)

需要考虑的条件:

  1. 字符串为空
  2. 正负号
  3. 字符串中除了数字和正负号,是否含有其他字符。
  4. 转换成整数是否会溢出。
    5. 字符串里面的数字是几进制的。

所以我们在写代码的时候要把这些都考虑上去。
现在我们来看下Integet.parseInt(string)是怎么处理的:

    public static int parseInt(String s) throws NumberFormatException {
        return parseInt(s,10);//将s转化为10进制的整数,其中第二个参数“10”是代表s原本是10进制的
    }

它是调用parseInt(s,10)这个函数,再进去这个函数看看,相应的解释已经在注释里面了:

    public static int parseInt(String s, int radix)
                throws NumberFormatException
    {
        /*
         * 将整数字符串s转换成10进制的整数
         * radix用来指明s是几进制
         */

        if (s == null) { //处理字符串s为空的情况
            throw new NumberFormatException("null");
        }

        //Character.MIN_RADIX为2,就是进制数radix小于2进制的话也是无效的
        if (radix < Character.MIN_RADIX) { 
            throw new NumberFormatException("radix " + radix +
                                            " less than Character.MIN_RADIX");
        }

        //Character.MAX_RADIX为36,就是进制数radix大于36进制的话也是无效的
        if (radix > Character.MAX_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " greater than Character.MAX_RADIX");
        }

        int result = 0; //最后的结果
        boolean negative = false; //判断正负号的标记,先初始化为正(negative = false),
        int i = 0, len = s.length();
        //初始化limit 为负的(MAX_VALUE = 0x7fffffff),因为下面每次的result是相减的形式,所以这里是﹣的
        **int limit = -Integer.MAX_VALUE;** 
        **int multmin;**
        int digit;

        if (len > 0) {
            char firstChar = s.charAt(0); //取出第一个字符判断时候包含正负号
            if (firstChar < '0') { // Possible leading "+" or "-"
                if (firstChar == '-') {
                    negative = true;
                    //若是字符串的符号是﹣,因为下面每次的result是相减的形式,所以这里是﹢的
                    **limit = Integer.MIN_VALUE;** 
                } else if (firstChar != '+')
                    throw NumberFormatException.forInputString(s);

                if (len == 1) // Cannot have lone "+" or "-"
                    throw NumberFormatException.forInputString(s);
                i++;
            }
            **multmin = limit / radix;** 
            while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                // 返回使用指定radix进制的字符 s.charAt(i++) 的数值
                digit = Character.digit(s.charAt(i++),radix);
                if (digit < 0) { // s.charAt(i++)的值是一个使用指定radix进制的无效数字,则返回 -1,异常
                    throw NumberFormatException.forInputString(s);
                }
                **if (result < multmin) {
                    throw NumberFormatException.forInputString(s);
                }**
                result *= radix; //上一次的结果乘以radix进制
                **if (result < limit + digit) { //处理溢出情况
                    throw NumberFormatException.forInputString(s);
                }**
                result -= digit; //和 return negative ? result : -result; 有关系
            }
        } else { //长度小于0的话,异常
            throw NumberFormatException.forInputString(s);
        }
        //negative是true的话,此整数是负的,上面result是减形式的(result -= digit),所以直接输出result 
        //negative是false的话,此整数是正的,输出-result
        return negative ? result : -result; 
    }

溢出问题是难点:
上述代码中对于一些非法输入都做了异常处理,主要需要看的地方是溢出的判断。

一般判断溢出会这样判断 digit > INT_MAX - result*10 (先不考虑正负问题),但是这段代码是有问题的。

我们知道INT_MAX = 2147483647, 如果输入的字符串是2147483657,那么执行到最后一位时,会有 result*10 = 2147483650 > INT_MAX,此时已经溢出,所以答案必然出错。

在July的博客中提到比较好的解决方法是用除法和取模。

  • 先用 result 与 INT_MAX /10 进行比较: 若 result > INT_MAX/10(当然同时还要考虑 result=INT_MAX/10 的情况), 说明最终得到的整数一定会溢出, 故此时可以当即进行溢出处理,从而也就免去了计算 result*10 这一步骤。
  • 当 result=INT_MAX/10 时,若 digit > INT_MAX%10,说明 result*10+digit 最终还是会溢出,也直接当溢出处理。
    比如,对于正数来说,溢出有两种可能:
    一种是诸如 2147483650,即 result > MAX/10 的;
    一种是诸如 2147483649,即 result == MAX/10 && digit > MAX%10 的。

由于INT_MAX = 2147483647 而 INT_MIN = -2147483648,两者绝对值不相等。因此,正负数溢出的条件不一样,代码中用了两个条件来判断溢出情况。
我们知道有符号 int 的上下限是-2147483648 ~ 2147483647,可见负数表达的范围比正数多一个,这样就好理解为什么在开头要把 limit 全部表达为负数(下限)。这样的操作减少了后续的判断,可以一步到位,相当于二者选择取其大一样,大的包含了小的,不用像我的代码一样正负数情况都判断一次。同理,那么 multmin 也都是负数了,而且可以认为是只和进制参数 radix 有关系。在这里 multmin 就是-INT_MAX/10,当负数时就是INT_MIN/10。所以与上文类似,第一个条件就是若-result < -INT_MAX/10则是溢出。不满足第一个条件的情况下,result10肯定不会溢出了。所以第二个条件判断若 - result10 < -INT_MAX + digit,则是溢出。

比如对于最大的负数来说,当扫描到最后一位时,result = -214748364,multmin=-214748364
第一个条件result < multmin不满足, 执行result *= radix之后,result = -2147483640
第二个条件result < limit + digit,即 -2147483640<-2147483648+8 也不满足条件。
所以正常输出。

猜你喜欢

转载自blog.csdn.net/weixin_44333359/article/details/89307316