<leetcode系列; String to Integer (atoi) 以及atoi源码实现

Posted 小贝也沉默

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了<leetcode系列; String to Integer (atoi) 以及atoi源码实现相关的知识,希望对你有一定的参考价值。

String to Integer (atoi)

Implement atoi to convert a string to an integer.

Hint: Carefully consider all possible input cases. If you want a challenge, please do not see below and ask yourself what are the possible input cases.

Notes: It is intended for this problem to be specified vaguely (ie, no given input specs). You are responsible to gather all the input requirements up front.

原题链接: https://leetcode.com/problems/string-to-integer-atoi/

题目大意: 类似于实现一个自己的atoi函数.

编译调试了很久, 遇到了不少感觉怪异的测试用例.列举如下:

测试用例期望值
“+-2”0
“10522545459”2147483647
” 105225454”105225454
“-2247483647”-2147483648
” 123adf”123
“ad12”0
” -123 5a43f”-123

还有一些没有列举出来. 大部分都是对数据溢出的处理问题.当数据溢出时, 正数的期望值是INT_MAX, 负数的期望值是INT_MIN.以及字符串开头的空格,符号位等.

其中数据溢出有两种情况.
1. 在已解析出的数值为214748364时, 而下一位的数值是8. 而int的最大值是2147483647.则数据溢出
2. 在已解析出的数值为314748364时, 下一位无论是任何数, 均会数据溢出.

最终提交代码如下:

int myAtoi(char* str) 
    if (NULL == str) 
        return 0;
    

    const int MAX_SIZE = 10;

    char* pStr = str;
    char* pSta = str;// 用于前置空格的排除
    bool sig = false; // false -- +; true -- -
    int num  = 0;
    int tmp  = 0;
    int count = 0; // 记录当前数值长度
    while ('\\0' != *pStr) 
        if ((*pStr >= '0') && (*pStr <= '9')) 
            if (MAX_SIZE == count)  // 数据溢出
                return sig ? INT_MIN : INT_MAX;
            
            int tmp = *pStr - '0';
            // 正负数分别计算
            num = sig ? (10 * num - tmp) : (10 * num + tmp);
            ++count;
         else 
            if (pStr == pSta) 
                switch (*pStr) 
                case '-':
                    sig = true;
                    break;
                case '+':
                    sig = false;
                    break;
                case ' ':
                    ++pSta;
                    break;
                default:
                    return 0;
                
             else 
                break;
            
        

        ++pStr;
    
    // 数据溢出
    if (sig && (num > 0)) 
        return INT_MIN;
     else if (!sig && (num < 0)) 
        return INT_MAX;
    

    return num;

显然, 在上述代码中, 肯定存在不少缺陷并且不够精炼.整个函数看起来繁琐, 各种标志位.效率当然也会大打折扣.

相信atoi函数对于大部分C/C++程序员都是一个比较熟悉的函数.在网站上查询了一下, atoi源代码确实写得很漂亮, 和我的代码比较起来, 简直一个天上一个地下啊.

可以看到atoi库函数的源码实现如下,供大家参阅:

int atoi(const char *nptr)

    return (int) strtol(nptr, (char **) NULL, 10);


long __XL(strtol)(const char * __restrict str, char ** __restrict endptr,
  int base   __LOCALE_PARAM )

    return __XL_NPP(_stdlib_strto_l)(str, endptr, base, 1   __LOCALE_ARG );


// 下述函数才是真正完成任务的函数
// 实际调用时, str 是输入的nptr, endptr 是 NULL, base 是 10, sflag 是 1
// 即 unsigned long  _XL_NPP(_stdlib_strto_l)(nptr, NULL, 10, 1)
unsigned long __XL_NPP(_stdlib_strto_l)(register const Wchar * __restrict str,
Wchar ** __restrict endptr, int base,
int sflag   __LOCALE_PARAM )

    unsigned long number, cutoff;
#if _STRTO_ENDPTR
    const Wchar *fail_char;
#define SET_FAIL(X)       fail_char = (X)
#else
#define SET_FAIL(X)       ((void)(X)) /* Keep side effects. */
#endif
    unsigned char negative, digit, cutoff_digit;
    assert(((unsigned int)sflag) <= 1);
    SET_FAIL(str);
    while (ISSPACE(*str))  /* 忽略str中开头的空格 */
        ++str;
    
    /* 0 代表 正数; 1 代表 负数 */
    negative = 0;
    switch(*str) 
    /* 注意到没有break, 无论*str是'+'或是'-',str均会自加 */
    case '-': negative = 1; 
    case '+': ++str;
    

    /* 上述操作结束后, 将str前面的空格或是符号位均已跳过.
       只跳过一个符号位,若出现"+-2"的情况, 那么当前*str是'-' */

    if (!(base & ~0x10))  /* 0x10的十进制是16, 当base等于16时, 该条件为真;否则为假;    */
    // 当然,还有可能有其他情况下会进入.比如base=0x******10(*代表任意数);
        base += 10; /* default is 10(26). */
        if (*str == '0') 
            SET_FAIL(++str);
            base -= 2;  /* Now base is 8 or 16 (24). */
            if ((0x20|(*str)) == 'x')  /* WARNING: assumes ascii. */
                ++str;
                base += base;   /* Base is 16 (16 or 48). */
            
        
        if (base > 16)     /* Adjust in case base wasn't dynamic. */
            base = 16;
        
    
    number = 0;
    if (((unsigned)(base - 2)) < 35)  /* 最大能计算的进制是36进制. */
        cutoff_digit = ULONG_MAX % base; // 计算当数值等于临界值时最大还能加上多大的数
        cutoff = ULONG_MAX / base; // 计算判断数据溢出的临界值
        do 
            // 判断每一位的数值; 代码执行类似如下
            /* if (((Wuchar)(*str - '0')) <= 9) 
                digit = *str - '0';
             else 
                if (*str >= 'A') 
                    // (0x20|(*str)) 将*str的第6位置1.
                    // 即将所有大写字母的ascii码变为小写字母的ascii码, 而小写字母则不变
                    // 不清楚的同学可以百度ascii码表, 字母的大小写ascii码只是在二进制的第六位不同.
                    // 大写字母的二进制第六位是0, 而小写的二进制第六位是1
                    digit = (((0x20|(*str)) - 'a' + 10));
                 else 
                    digit = 40; // 该位是非法字符.
                
             */
            digit = (((Wuchar)(*str - '0')) <= 9)
                ? (*str - '0')
                : ((*str >= 'A')
                   ? (((0x20|(*str)) - 'a' + 10)) /* WARNING: assumes ascii. */
                  : 40);

            // 遇到非法字符, 如16进制, 遇到了'?', 10进制,遇到了'A'
            if (digit >= base) 
                break;
            
            SET_FAIL(++str);
            // 判断加上该位的数值后数据溢出
            if ((number > cutoff)
                || ((number == cutoff) && (digit > cutoff_digit))) 
                number = ULONG_MAX;
                negative &= sflag;
                SET_ERRNO(ERANGE);
             else  // 未溢出
                number = number * base + digit;
            
         while (1);
    
#if _STRTO_ENDPTR
    if (endptr) 
        *endptr = (Wchar *) fail_char;
    
#endif
    
        // ((unsigned long)(-(1+LONG_MIN))) == LONG_MAX, 但不知道为什么要这样获取long的最大值.
        // 当欲计算的数为负数时, tmp = LONG_MAX + 1;为正数时, tmp = LONG_MAX
        unsigned long tmp = ((negative)
             ? ((unsigned long)(-(1+LONG_MIN)))+1
             : LONG_MAX);
        if (sflag && (number > tmp)) 
            number = tmp;
            SET_ERRNO(ERANGE);
        
    
    return negative ? (unsigned long)(-((long)number)) : number;

以上是关于<leetcode系列; String to Integer (atoi) 以及atoi源码实现的主要内容,如果未能解决你的问题,请参考以下文章

leetCode题解之Number of Lines To Write String

leetcode problem 8: String to Integer (atoi)

LeetCode 8. String to Integer

Leetcode--String to Integer (atoi)

Leetcode13 Roman To Integer

Leetcode-926 Flip String to Monotone Increasing(将字符串翻转到单调递增)