如何在Go中将字符串转换为整数类型?

446

我正在尝试将从flag.Arg(n)返回的字符串转换为int。在Go中,惯用的方法是什么?


8
strconv.Itoa(i) 是将整型转换为字符串的函数。参见 https://dev59.com/e2Yr5IYBdhLWcg3w0taE#62737936。 strconv.Atoi(s) 是将字符串转换为整型的函数。参见 https://dev59.com/OVwZ5IYBdhLWcg3waPvX#62740786。 - user12817546
5个回答

547

例如strconv.Atoi

代码:

package main

import (
    "fmt"
    "strconv"
)

func main() {
    s := "123"

    // string to int
    i, err := strconv.Atoi(s)
    if err != nil {
        // ... handle error
        panic(err)
    }

    fmt.Println(s, i)
}

2
strconv 可以被解释为 _字符串转换_,同样地,是否有任何意义(扩展)可以使 Atoi 更容易记忆。 - Roshana Pitigala
11
@RoshanaPitigala所说的"A"代表字母表中由字符串表示的A到Z。然后是"i"代表整数。A到i,这是一种方式。另一种方式是了解"atoi"的起源,它显然是"ASCII转整数":stackoverflow.com/a/2909772/1544473 - Adé
2
在一个创建于 UNICODE 时代的语言中,Go 语言的创造者能想到的最好的助记符竟然是“Alphabet-to-Integer”(这是具有误导性的;十进制数字不是字母字符)或者“ASCII-to-Integer”(已经过时,很快就会被遗弃)。在我看来,“Character-to-Integer”、“String-to-Integer”甚至是“Unicode-to-Integer”都会是更好的助记符。反思一下,似乎他们只是模仿了 C 语言惯例,包括一个 atoi() 库函数。 - BalRog
当然,他们会模仿C语言的惯例。这是一个好习惯,它遵循最少惊讶原则。 - MarioVilas

142

转换简单字符串

最简单的方法是使用strconv.Atoi()函数。

请注意,还有许多其他方法。例如fmt.Sscan()strconv.ParseInt(),它们提供更大的灵活性,因为您可以指定进制和位数等。正如在strconv.Atoi()的文档中所述:

Atoi等效于ParseInt(s, 10, 0),转换为int类型。

下面是使用上述函数的示例(在Go Playground上尝试):

flag.Parse()
s := flag.Arg(0)

if i, err := strconv.Atoi(s); err == nil {
    fmt.Printf("i=%d, type: %T\n", i, i)
}

if i, err := strconv.ParseInt(s, 10, 64); err == nil {
    fmt.Printf("i=%d, type: %T\n", i, i)
}

var i int
if _, err := fmt.Sscan(s, &i); err == nil {
    fmt.Printf("i=%d, type: %T\n", i, i)
}

输出(如果使用参数"123"):

i=123, type: int
i=123, type: int64
i=123, type: int

解析自定义字符串

还有一个方便的fmt.Sscanf()函数,它提供了更大的灵活性,因为你可以在格式字符串中指定数字格式(如宽度、基数等),并在输入的string中添加额外的字符。

这对于解析包含数字的自定义字符串非常有用。例如,如果你的输入以"id:00123"的形式提供,其中你有一个前缀"id:"和固定的5位数字,如果较短则用零填充,那么可以很容易地进行解析,如下所示:

s := "id:00123"

var i int
if _, err := fmt.Sscanf(s, "id:%5d", &i); err == nil {
    fmt.Println(i) // Outputs 123
}

ParseInt 的第二个参数是什么意思? - kaushik94
2
点击strconv.ParseInt()链接,你会立刻看到:ParseInt(s string, base int, bitSize int)。所以它是基于这个参数:_"ParseInt使用给定的进制(2到36)来解释一个字符串s"_。 - icza
请注意,strconv.ParseInt()函数中的bitSize参数不会将字符串转换为您选择的类型,而只是用于限制结果的特定位数。另请参阅:https://stackoverflow.com/questions/55925894/parseint-doesnt-convert-to-the-desired-type/55926193 - viv
@viv 是的,没错。如果需要 int 类型的值并使用 strconv.ParseInt(),则需要手动进行类型转换(从 int64int)。 - icza

61

以下是三种将字符串解析为整数的方法,按速度从快到慢排序:

  1. strconv.ParseInt(...) 最快
  2. strconv.Atoi(...) 仍然非常快
  3. fmt.Sscanf(...) 不是特别快,但最灵活

下面是一个基准测试,显示每个函数的使用方法和示例时间:

package main

import "fmt"
import "strconv"
import "testing"

var num = 123456
var numstr = "123456"

func BenchmarkStrconvParseInt(b *testing.B) {
  num64 := int64(num)
  for i := 0; i < b.N; i++ {
    x, err := strconv.ParseInt(numstr, 10, 64)
    if x != num64 || err != nil {
      b.Error(err)
    }
  }
}

func BenchmarkAtoi(b *testing.B) {
  for i := 0; i < b.N; i++ {
    x, err := strconv.Atoi(numstr)
    if x != num || err != nil {
      b.Error(err)
    }
  }
}

func BenchmarkFmtSscan(b *testing.B) {
  for i := 0; i < b.N; i++ {
    var x int
    n, err := fmt.Sscanf(numstr, "%d", &x)
    if n != 1 || x != num || err != nil {
      b.Error(err)
    }
  }
}

你可以将其保存为atoi_test.go并运行go test -bench = . atoi_test.go 来运行它。

goos: darwin
goarch: amd64
BenchmarkStrconvParseInt-8      100000000           17.1 ns/op
BenchmarkAtoi-8                 100000000           19.4 ns/op
BenchmarkFmtSscan-8               2000000          693   ns/op
PASS
ok      command-line-arguments  5.797s

清晰明了的回答。我在这里引用了您的话https://dev59.com/OVwZ5IYBdhLWcg3waPvX#62740786。 - user12817546
1
Atoi相当于将ParseInt(s,10,0)转换为int类型。 - gondo

8

试试这个

import ("strconv")

value := "123"
number,err := strconv.ParseUint(value, 10, 32)
finalIntNum := int(number) //Convert uint64 To int

这段代码会在 int 是32位的平台上,默默地将在 uint32 范围内但不在 int 范围内的值进行转换,且没有发生错误。例如:值 "4234567890" 将被转换为 -60399406Go Playground (64 bits) - dolmen

2

如果您控制输入数据,可以使用迷你版本。

package main

import (
    "testing"
    "strconv"
)

func Atoi (s string) int {
    var (
        n uint64
        i int
        v byte
    )   
    for ; i < len(s); i++ {
        d := s[i]
        if '0' <= d && d <= '9' {
            v = d - '0'
        } else if 'a' <= d && d <= 'z' {
            v = d - 'a' + 10
        } else if 'A' <= d && d <= 'Z' {
            v = d - 'A' + 10
        } else {
            n = 0; break        
        }
        n *= uint64(10) 
        n += uint64(v)
    }
    return int(n)
}

func BenchmarkAtoi(b *testing.B) {
    for i := 0; i < b.N; i++ {
        in := Atoi("9999")
        _ = in
    }   
}

func BenchmarkStrconvAtoi(b *testing.B) {
    for i := 0; i < b.N; i++ {
        in, _ := strconv.Atoi("9999")
        _ = in
    }   
}

最快的选择(如果必要,请填写您的支票)。结果:

Path>go test -bench=. atoi_test.go
goos: windows
goarch: amd64
BenchmarkAtoi-2                 100000000               14.6 ns/op
BenchmarkStrconvAtoi-2          30000000                51.2 ns/op
PASS
ok      path     3.293s

1
什么?真的吗?写“go”语言的人让很多事情变得容易了。不要浪费时间啦 :) - Balaji Boggaram Ramanarayan
Atoi("-9999")怎么样? - Oleksiy Chechel
这个实现缺乏文档,没有提到未处理的边缘情况(例如:负十进制整数)。我甚至没有提到缺乏测试。 - dolmen
1
如果有人尽管上面的评论还是想使用这个,请注意它的基数是硬编码为10,但它解码字母就像它是任意基数一样。如果你试图用它解码十六进制,这将会产生奇怪的结果。 - Andrew
一个正确的基准测试应该包括各种数字,以查看十进制和十六进制之间是否存在差异。此外,十六进制仅限于“F”(而不是“Z”),需要使用“n *= 16”而不是10... - Alexis Wilke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接