<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--String to Integer (atoi)
Leetcode-926 Flip String to Monotone Increasing(将字符串翻转到单调递增)