真题描述:请你来实现一个 atoi 函数,使其能将字符串转换成整数。 首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。 当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。 该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。 注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。 在任何情况下,若函数不能进行有效的转换时,请返回 0。

说明: 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−2^31, 2^31 − 1]。如果数值超过这个范围,请返回 INT_MAX (2^31 − 1) 或 INT_MIN (−2^31) 。

示例 1: 输入: “42” 输出: 42

示例 2: 输入: “ -42” 输出: -42 解释: 第一个非空白字符为 ‘-‘, 它是一个负号。 我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。

示例 3: 输入: “4193 with words” 输出: 4193 解释: 转换截止于数字 ‘3’ ,因为它的下一个字符不为数字。

示例 4: 输入: “words and 987” 输出: 0 解释: 第一个非空字符是 ‘w’, 但它不是数字或正、负号。 因此无法执行有效的转换。

示例 5: 输入: “-91283472332” 输出: -2147483648 解释: 数字 “-91283472332” 超过 32 位有符号整数范围。因此返回 INT_MIN (−2^31) 。

思路:

  1. 该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止——暗示你拿到字符串先去空格;
  2. 当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号——暗示你识别开头的“+”字符和“-”字符;
  3. 该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响——暗示你见到非整数字符就刹车;
  4. 说明: 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−2^31, 2^31 − 1]。如果数值超过这个范围,请返回 INT_MAX (2^31 − 1) 或 INT_MIN (−2^31) ——暗示……这都不是暗示了,这是明示啊!直接告诉你先把这俩边界值算出来,摆在那做卡口就完了。

计算卡口

计算某个数的 n 次方,我们要用到 Math.pow 这个方法:

  1. // 计算最大值
  2. const max = Math.pow(2,31) - 1
  3. // 计算最小值
  4. const min = -max - 1

解析字符串

在这道题里,我们需要从字符串中提取的其实只有“+/-”符号以及其后面的数字而已,同时这个字符串需要满足 可能存在的空格+正负号+数字字符串+其它字符内容 这样的格式才算合法,那我们就可以通过这样写正则表达式,实现“匹配”和“提取”的双重目的:

  1. /\s*([-\+]?[0-9]*).*/
  • 首先,\s 这个符号,意味着空字符,它可以用来匹配回车、空格、换行等空白区域,这里,它用来被匹配空格。*这个符号,跟在其它符号后面,意味着“前面这个符号可以出现0次或多次。\s*,这里的意思就是空格出现0次或多次,都可被匹配到。
  • 接着 () 出现了。() 圈住的内容,就是我们要捕获起来额外存储的东西。
  • []中的匹配符之间是“或”的关系,也就是说只要能匹配上其中一个就行了。这里[]中包括了-\+-不必说匹配的是对应字符,这个\+之所以加了一个斜杠符,是因为+本身是一个有特殊作用的正则匹配符,这里我们要让它回归+字符的本义,所以要用一个\来完成转义。
  • [0-9]*结合咱们前面铺陈的知识,这个就不难理解了,它的意思是 0-9 之间的整数,能匹配到0个或多个就算匹配成功。
  • 最后的 .这个是任意字符的意思,.*用于字符串尾部匹配非数字的任意字符。我们看到.*是被排除捕获组之外的,所以说这个东西其实也不会被额外存储,它被“摘除”了。

获取捕获结果

JS 的正则相关方法中, test()方法返回的是一个布尔值,单纯判断“是否匹配”。要想获取匹配的结果,我们需要调度match()方法:

  1. const reg = /\s*([-\+]?[0-9]*).*/
  2. const groups = str.match(reg)

match() 方法是一个在字符串中执行查找匹配的String方法,它返回一个数组,在未匹配到时会返回 null。
如果我们的正则表达式尾部有 g 标志,match()会返回与完整正则表达式匹配的所有结果,但不会返回捕获组
这里我们没有使用g标志,match()就会返回第一个完整匹配(作为数组的第0项)及其相关的捕获组(作为数组的第1及第1+项)。
这里我们只定义了一个捕获组,因此可以从 groups[1] 里拿到我们捕获的结果。

判断卡口

把捕获的结果转换成数字,看看是否超出了题目要求的范围。这一步比较简单,无需多言。

  1. // 入参是一个字符串
  2. const myAtoi = function(str) {
  3. // 编写正则表达式
  4. const reg = /\s*([-\+]?[0-9]*).*/
  5. // 得到捕获组
  6. const groups = str.match(reg)
  7. // 计算最大值
  8. const max = Math.pow(2,31) - 1
  9. // 计算最小值
  10. const min = -max - 1
  11. // targetNum 用于存储转化出来的数字
  12. let targetNum = 0
  13. // 如果匹配成功
  14. if(groups) {
  15. // 尝试转化捕获到的结构
  16. targetNum = +groups[1]
  17. // 注意,即便成功,也可能出现非数字的情况,比如单一个'+'
  18. if(isNaN(targetNum)) {
  19. // 不能进行有效的转换时,请返回 0
  20. targetNum = 0
  21. }
  22. }
  23. // 卡口判断
  24. if(targetNum > max) {
  25. return max
  26. } else if( targetNum < min) {
  27. return min
  28. }
  29. // 返回转换结果
  30. return targetNum
  31. };