图解LeetCode:如何把字符串转为数字
这是LeetCode中的第8号题目,String to Integer (atoi),也就是字符串转数字,我们常用的库函数atoi就是用来进行该转换的,有意思的是这个题目点赞的只有1000+,不喜欢的有7000+:

实际上从题目的通过率就可以看出来,这个题目的通过率只有百分之十几,很显然是大家提交了N多次都不通过才点的不喜欢吧。

为什么这个题目的通过率这么低呢?是这个题目很难吗?不是的,题目很简单,实现起来也没有什么技巧可言,这个题目难在需要考虑的点很多,题目要求:
给定的字符串开头可能有很多空白字符,因此需要在真正将字符转为数字之前需要首先过滤掉空白字符
给定的字符串可能包含有 "+" 或者 "-" ,用来表示这是正数还是负数
如果第一个非空白字符既不是"+"、"-",也不是数字,那么返回0
在将字符转为数字的过程中,如果遇到非数字字符则停止转换
如果转换后的数字大于int能表示的最大数,则直接返回INT_MAX (2^31 − 1)
如果转换后的数字小于int能表示的最小数,则直接返回INT_MIN (−2^31)
那么我们该怎么解决这个问题呢?
实现思路
由于这个问题的解决非常直观,就是把字符串转为数字,因此只需要实现细节,接下来我们就以字符串“ -6082”为例来讲解,该字符串我们记为变量str,并用变量i记录我们已经遍历到哪个位置了,我们使用"_"来表示空表字符,如图所示:

首先我们需要过滤掉字符串开头中可能存在的空白字符,因此我们需要判断当前字符是不是空白符:
int i=0;while(i' ' )++i;当这段代码执行完毕后,我们来到了第一个非空表字符,如图所示:

此时我们需要判断以下几件事:
A,第一个非空字符是不是"+",如果是,则说明这是个正数
B,第一个非空字符是不是"-",如果是,则说明这是个负数
C,第一个非空字符是不是数字,如果不是则直接返回
因此,我们需要定义一个变量sign,用来表示该数字是正数还是负数:
int sign = 1; if (str[i] == '+') // A ++i; else if (str[i] == '-'){ // B ++i; sign = -1; } else if (!('0'<=str[i] && str[i]<='9')) { // C return 0; }由于当前字符是"-",因此变量sign为-1。这样我们来到了第一个既不是空白字符也不是正负号的字符,如果这个位置是数字,那么我们就要开始将字符转为数字了,为此我们需要定义变量long int r=0,用于记录转换后的数字,如图所示:

之所以定义为long int是因为需要判断当前数字是不是已经超出了int能表示的最大范围,那么该如何依次将字符转为数字呢?使用以下代码:
r = r * 10 + 当前字符所代表的数字接下来你就明白这句代码的意思了,由于当前数字是6,因此:
r = 0 * 10 + 6
如图所示:

现在你应该能明白接下来几个数字是怎么计算了吧:

在计算r的过程中我们还要不断判断r是否已经超过了int能表示的最大范围,如果超过了那么停止转换并直接返回int能表示的最大数,并且如果当前字符不是数字的话也直接返回r,这个过程用代码表示就是:
long int r=0;while(i if ('0'<=str[i] && str[i]<='9') {r=r*10+str[i]-'0';} else {return r*sign;}if (r*sign > (int)0x7fffffff)return 0x7fffffff;if (r*sign < (int)0x80000000)return 0x80000000;++i;}现在你应该明白这整个过程了吧。
代码实现
上述分析过程的完整代码实现是:
int atoi(string str) { int len = str.length(); if (len == 0) return 0; int i=0; // 过滤掉空格 while(i<len && str[i] == ' ') ++i; if (i == len) return 0; int sign = 1; if (str[i] == '+') ++i; else if (str[i] == '-'){ ++i; sign = -1; } else if (!('0'<=str[i] && str[i]<='9')) { return 0; }
long int r=0; while(i<len){ if ('0'<=str[i] && str[i]<='9') { r=r*10+str[i]-'0'; } else { return r*sign; } if (r*sign > (int)0x7fffffff) return 0x7fffffff; if (r*sign < (int)0x80000000) return 0x80000000; ++i; } return r*sign;}总结
这个题目本身思路并不难,但是难在对细节的处理,代码中要判断各种情况,当你下次再使用各种库函数的时候(比如atoi())是不是应该感谢一下库的作者帮我们处理这这么多琐碎的事情,而实际上软件工程也正是基于这样的逻辑建立起来的,那就是“站在巨人的肩膀之上”。
现在,使用任何语言写一个最简单的程序,哪怕只是一个空的main函数,这样一个最简单程序的运行背后是无数编程大牛努力的结果,这背后是操作系统、编译器、解释器、编程语言标准库,正是这些基石使得现在的程序员可以只关注解决自己特定的问题,创世的工作已经有人完成,剩下的舞台就交给你了。





