历史问题

Q:明明是 string to int 为什么是 Atoi? A: Atoi (Ascii to Integer),Ascii 即上古时期的 string,流传至今

题目

请你来实现一个 atoi 函数,使其能将字符串转换成整数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。

当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。

该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0。

说明:

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

实现

package problem0008

import "strings"
import "math"

func myAtoi(s string) int {
    return convert(clean(s))
}

func clean(s string) (sign int, abs string) {
    // 除去首尾的空格
    s = strings.TrimSpace(s)
    if s == "" {
        return
    }


    switch s[0] {
    case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
        sign, abs = 1, s
    case '+':
        sign, abs = 1, s[1:]
    case '-':
        sign, abs = -1, s[1:]
    default:
        abs = ""
        return
    }

    // 不完全是数字了 仅截取数字部分
    for i, b := range abs {
        if b < '0' || '9' < b {
            abs = abs[:i]
            break
        }
    }

    return
}

func convert(sign int, absStr string) int {
    abs := 0

    for _, b := range absStr {
        abs = abs*10 + int(b-'0')
        // 检查溢出
        switch {
        case sign == 1 && abs > math.MaxInt32:
            return math.MaxInt32
        case sign == -1 && sign*abs < math.MinInt32:
            return math.MinInt32
        }
    }

    return sign * abs
}

单元测试

这道题如果有思路会发现,实现不难主要是特殊情况比较多,单元测试需列举所有情况:

package problem0008

import (
    "testing"

    "math"

    "github.com/stretchr/testify/assert"
)

type para struct {
    one string
}

type ans struct {
    one int
}

type question struct {
    p para
    a ans
}

func Test_OK(t *testing.T) {
    ast := assert.New(t)

    qs := []question{
        // 超过边界最大
        question{

            p: para{
                one: "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000522545459",
            },
            a: ans{
                one: math.MaxInt32,
            },
        },
        // 正常
        question{
            p: para{
                one: "123",
            },
            a: ans{
                one: 123,
            },
        },
        // 正常负数
        question{
            p: para{
                one: "-123",
            },
            a: ans{
                one: -123,
            },
        },
        // 最大正数
        question{
            p: para{
                one: "2147483648",
            },
            a: ans{
                one: math.MaxInt32,
            },
        },
        // 最大负数
        question{
            p: para{
                one: "-2147483649",
            },
            a: ans{
                one: math.MinInt32,
            },
        },
        // 中间有字母
        question{
            p: para{
                one: " 1234a6789",
            },
            a: ans{
                one: 1234,
            },
        },
        // 负数 中间有字母
        question{
            p: para{
                one: "  -0012a42 ",
            },
            a: ans{
                one: -12,
            },
        },
        // 全字母
        question{
            p: para{
                one: " asdfdfs ",
            },
            a: ans{
                one: 0,
            },
        },
        // 空格
        question{
            p: para{
                one: " ",
            },
            a: ans{
                one: 0,
            },
        },
        // 数字 和 空格
        question{
            p: para{
                one: " +1   ",
            },
            a: ans{
                one: 1,
            },
        },

        // 只有符号
        question{
            p: para{
                one: "-",
            },
            a: ans{
                one: 0,
            },
        },
        // 超过最大值
        question{
            p: para{
                one: "922337999995452345782348957234895793875923845789234758923745987239485798345789237598235980234859023849058349058903890869059068490683490869038690385690385906839056890548690586904568905468905908590839056890345869034856903568903854690835906834906839045869034869034568903458690356903569056908345906839056890586903546890345869034568903586905689054685690905689035468905689056890879056907890879086903548690387905469054690890689035869038569034856908356908345906890345869056890356890358690569083546908549086905690345869038569034569083590689058690385690358690586908345906839086390689056903869058690345869038690586908569054690834590689054869083569035490689035689058690586905409689086905869038569083549068390468903586903569038549068905869054690345906890346904856903546908345906890568903569054690590685906905689058690586905869056890869035869035890789068790907903890835657428975457575789075098759084752897589475029847589047589234759028475902847592834752908759827589725987517891598715908749871908579841790817598715901875901874190879085791879018571897491837249874987235987589734897123489712390847913857190287549018735902036854775809",
            },
            a: ans{
                one: math.MaxInt32,
            },
        },
    }

    for _, q := range qs {
        a, p := q.a, q.p
        ast.Equal(a.one, myAtoi(p.one), "输入:%v", p)
    }
}

参考

  1. C 中的 Atoi 是什么意思
  2. Where did the name atoi come from?
  3. Leetcode
  4. leetcode-in-go