Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 414|回复: 0

[Java基础知识]parseInt的实现(包含java和c语言)

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-9-30 17:45:03 | 显示全部楼层 |阅读模式
    jdk中的Integer类是int对象的包装类,正常的Integer占用内存开销要比int大,比例大概是1:4 。
       今天分享的代码是Integer类中的静态方法parseInt(String, int)。这个方法众所周知, 甚至在我们一开始学习编程时就尝试的写过这样的代码,一个正常的思路:
    遍历输入的字符数组(java的字符串就是一个字符数组), 然后parse每个char,依据参数给定的进制,判断每个char是否满足,满足则继续, 否则抛出异常或中断,直到处理完毕所有字符,返回结果。
        那么我们看看jdk给出的实现:
            public static int parseInt(String s, int radix) throws NumberFormatException
         {
             if (s == null) {
                 throw new NumberFormatException("null");
             }
            if (radix < Character.MIN_RADIX) {
             throw new NumberFormatException("radix " + radix +
                             " less than Character.MIN_RADIX");
         }
            if (radix > Character.MAX_RADIX) {
             throw new NumberFormatException("radix " + radix +
                             " greater than Character.MAX_RADIX");
         }
            int result = 0;
         boolean negative = false;
         int i = 0, max = s.length();
         int limit;
         int multmin;
         int digit;
            if (max > 0) {
             if (s.charAt(0) == "-") {
             negative = true;
             limit = Integer.MIN_VALUE;
             i++;
             } else {
             limit = -Integer.MAX_VALUE;
             }
             multmin = limit / radix;
             if (i < max) {
             digit = Character.digit(s.charAt(i++),radix);
             if (digit < 0) {
                 throw NumberFormatException.forInputString(s);
             } else {
                 result = -digit;
             }
             }
             while (i < max) {
             // Accumulating negatively avoids surprises near MAX_VALUE
             digit = Character.digit(s.charAt(i++),radix);
             if (digit < 0) {
                 throw NumberFormatException.forInputString(s);
             }
             if (result < multmin) {
                 throw NumberFormatException.forInputString(s);
             }
             result *= radix;
             if (result < limit + digit) {
                 throw NumberFormatException.forInputString(s);
             }
             result -= digit;
             }
         } else {
             throw NumberFormatException.forInputString(s);
         }
         if (negative) {
             if (i > 1) {
             return result;
             } else {    /* Only got "-" */
             throw NumberFormatException.forInputString(s);
             }
         } else {
             return -result;
         }
         }
        过程就是按照思路来的,但是更全面一些,首先做一些参数检查,然后定义了局部变量用于计算:
    result是对应的int结果,negative对应是否是负数的判断,i是遍历用的索引指针,max代表字符串的长度,
    limit是合法数字的上限(下限),digit是当前扫描到的字符对应的数字,multmin是在做乘法计算时能走到的合法下限。
        严谨是这段程序最大的特点,因为有符号int的上下限是-2147483648~2147483647,可见负数表达的范围比正数多一个,
    这样就好理解为什么在开头要把limit全部表达为负数(下限),这样的操作减少了后续的判断,可以一步到位,
    相当于二者选择取其大一样,大的包含了小的。同理,那么multmin也就是负数了,
    而且可以认为是只和进制参数radix有关系。接着每个char的扫描计算digit利用到了Character.digit(char,int) 方法,
    这个方法就是在调用CharacterDataLatin1.digit(codePoint, radix) 方法,
    而这个新的方法其实只是去静态数组中取个映射而已。最后当顺利的执行完while循环后,result结果也就计算好了。
        作为程序设计人员,我最初接触的语言是C++,当初用到的库函数是atoi,那么我们看看atoi的库标准实现:
       
    1. int
    2. atoi(str)
    3.         const char *str;
    4. {
    5.         _DIAGASSERT(str != NULL);
    6.         return((int)strtol(str, (char **)NULL, 10));
    7. }
    复制代码

       
    1. 其中调用了strtol方法,参数传递的radix是10,也就是说我们常用的atoi是默认转化字符串到10进制的。
    2. 其中开始时还进行了一个trim的操作,而且支持16进制的0x开头,可谓完全的尽善尽美啊。
    复制代码

       
    1. strtol方法:
    复制代码

       
    1. #define        _FUNCNAME        strtol
    2. #define        __INT                long
    3. #define        __INT_MIN        LONG_MIN
    4. #define        __INT_MAX        LONG_MAX
    复制代码

       
    1. __INT
    2. _FUNCNAME(const char *nptr, char **endptr, int base)
    3. {
    4.         const char *s;
    5.         __INT acc, cutoff;
    6.         char c;
    7.         int i, neg, any, cutlim;
    8.         _DIAGASSERT(nptr != NULL);
    9.         /* endptr may be NULL */
    10.         /* check base value */
    11.         if (base && (base < 2 || base > 36)) {
    12.                 errno = EINVAL;
    13.                 return(0);
    14.         }
    15.         /*
    16.          * Skip white space and pick up leading +/- sign if any.
    17.          * If base is 0, allow 0x for hex and 0 for octal, else
    18.          * assume decimal; if base is already 16, allow 0x.
    19.          */
    20.         s = nptr;
    21.         do {
    22.                 c = *s++;
    23.         } while (isspace(c));
    24.         if (c == "-") {
    25.                 neg = 1;
    26.                 c = *s++;
    27.         } else {
    28.                 neg = 0;
    29.                 if (c == "+")
    30.                         c = *s++;
    31.         }
    32.         if ((base == 0 || base == 16) &&
    33.             c == "0" && (*s == "x" || *s == "X")) {
    34.                 c = s[1];
    35.                 s += 2;
    36.                 base = 16;
    37.         }
    38.         if (base == 0)
    39.                 base = c == "0" ? 8 : 10;
    40.         /*
    41.          * Compute the cutoff value between legal numbers and illegal
    42.          * numbers.  That is the largest legal value, divided by the
    43.          * base.  An input number that is greater than this value, if
    44.          * followed by a legal input character, is too big.  One that
    45.          * is equal to this value may be valid or not; the limit
    46.          * between valid and invalid numbers is then based on the last
    47.          * digit.  For instance, if the range for longs is
    48.          * [-2147483648..2147483647] and the input base is 10,
    49.          * cutoff will be set to 214748364 and cutlim to either
    50.          * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
    51.          * a value > 214748364, or equal but the next digit is > 7 (or 8),
    52.          * the number is too big, and we will return a range error.
    53.          *
    54.          * Set any if any `digits" consumed; make it negative to indicate
    55.          * overflow.
    56.          */
    57.         cutoff = neg ? __INT_MIN : __INT_MAX;
    58.         cutlim = (int)(cutoff % base);
    59.         cutoff /= base;
    60.         if (neg) {
    61.                 if (cutlim > 0) {
    62.                         cutlim -= base;
    63.                         cutoff += 1;
    64.                 }
    65.                 cutlim = -cutlim;
    66.         }
    67.         for (acc = 0, any = 0;; c = *s++) {
    68.                 if (!isascii(c))
    69.                         break;
    70.                 if (isdigit(c))
    71.                         i = c - "0";
    72.                 else if (isalpha(c))
    73.                         i = c - (isupper(c) ? "A" - 10 : "a" - 10);
    74.                 else
    75.                         break;
    76.                 if (i >= base)
    77.                         break;
    78.                 if (any < 0)
    79.                         continue;
    80.                 if (neg) {
    81.                         if (acc < cutoff || (acc == cutoff && i > cutlim)) {
    82.                                 any = -1;
    83.                                 acc = __INT_MIN;
    84.                                 errno = ERANGE;
    85.                         } else {
    86.                                 any = 1;
    87.                                 acc *= base;
    88.                                 acc -= i;
    89.                         }
    90.                 } else {
    91.                         if (acc > cutoff || (acc == cutoff && i > cutlim)) {
    92.                                 any = -1;
    93.                                 acc = __INT_MAX;
    94.                                 errno = ERANGE;
    95.                         } else {
    96.                                 any = 1;
    97.                                 acc *= base;
    98.                                 acc += i;
    99.                         }
    100.                 }
    101.         }
    102.         if (endptr != 0)
    103.                 /* LINTED interface specification */
    104.                 *endptr = __DECONST(char *, (any ? s - 1 : nptr));
    105.         return(acc);
    106. }
    复制代码

       
    复制代码

       
    1. 当然,类似的代码还有很多,这里只列出了两大语言的库实现,总体思路是一致的,
    2. 当我们设计api时,这种编程思路和风格以及功能的考虑是我们需要学习的。
    复制代码

       
    1. 下面这两篇stackoverflow的问答给出了一些比较全面的c风格代码,可以参考,这里不贴全文只给link:
    复制代码

       
    1. [url=http://stackoverflow.com/questions/194465/how-to-parse-a-string-to-an-int-in-c]http://stackoverflow.com/questions/194465/how-to-parse-a-string-to-an-int-in-c[/url]
    复制代码

       
    1. [url=http://stackoverflow.com/questions/4442658/c-parse-int-from-string]http://stackoverflow.com/questions/4442658/c-parse-int-from-string[/url]
    复制代码

       
    1. 参考文献:
    复制代码

       
    1. jdk文档及源码
    复制代码

       
    1. c库函数源码及文档
    复制代码
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-20 16:10 , Processed in 0.397731 second(s), 46 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表