首页 文章资讯内容详情

golang字符串

2026-06-01 4 花语

string类型单独提取为一篇教程是因为在Go中,string的实现方式同其他语言的不同。

什么是字符串

在Go中字符串是byte数组。可以通过将内容放在双引号""之间的方式来创建一个字符串。让我们看一个简单的例子,该例子创建并打印一个字符串:

packagemain import( "fmt" ) funcmain(){ name:="HelloWorld" fmt.Println(name) }12345678910

上面的[程序]输出:HelloWorld。

Go中的字符串符合Unicode标准,并以UTF-8编码。

访问字符串中的字节

因为字符串是字节数组,因此可以访问一个字符串中的字节。

packagemain import( "fmt" ) funcprintBytes(sstring){ fori:=0;i<len(s);i++{ fmt.Printf("%x",s[i]) } } funcmain(){ name:="HelloWorld" printBytes(name) }12345678910111213141516

在上面的程序中,len(s)返回字符串中的字节数,我们用一个for循环以16进制打印这些字节。%x格式化指示符用来以16进制打印参数。上面的程序打印:48656c6c6f20576f726c64。它们是"HelloWorld"以UTF-8方式编码的Unicode值。对Unicode字符集和UTF-8编码有一个基本的了解会更好的理解string类型。我(原文作者)建议大家阅读:https://naveenr.net/unicode-character-set-and-utf-8-utf-16-utf-32-encoding/来学习什么是Unicode和UTF-8。

让我们修改上面的程序以打印字符串中的字符:

packagemain import( "fmt" ) funcprintBytes(sstring){ fori:=0;i<len(s);i++{ fmt.Printf("%x",s[i]) } } funcprintChars(sstring){ fori:=0;i<len(s);i++{ fmt.Printf("%c",s[i]) } } funcmain(){ name:="HelloWorld" printBytes(name) fmt.Printf("\n") printChars(name) }12345678910111213141516171819202122232425

在第16行的printChars函数中,%c格式化指示符用来打印字符串中的字符。上面的程序输出为:

48656c6c6f20576f726c64 HelloWorld12

虽然上面的程序看起来是一种合法的打印字符串中各个字符的方法,但是这里有一个严重的错误。让我们深入这段代码看看究竟是哪里不对。

packagemain import( "fmt" ) funcprintBytes(sstring){ fori:=0;i<len(s);i++{ fmt.Printf("%x",s[i]) } } funcprintChars(sstring){ fori:=0;i<len(s);i++{ fmt.Printf("%c",s[i]) } } funcmain(){ name:="HelloWorld" printBytes(name) fmt.Printf("\n") printChars(name) fmt.Printf("\n") name="Señor" printBytes(name) fmt.Printf("\n") printChars(name) }1234567891011121314151617181920212223242526272829

上面程序的输出为:

48656c6c6f20576f726c64 HelloWorld 5365c3b16f72 Señor12345

在上面的程序第28行,我们试图打印Señor中的字符串,但是结果却是Señor,这明显是错的。为什么打印HelloWorld是对的,打印Señor是错的呢?原因是ñ的Unicode码点是U+00F1而它的UTF-8编码占了两个字节:c3和b1。我们试图将一个字节视为一个字符来打印字符串的方法是错误的。在UTF-8编码中一个码点编码后可能占多于1个字节的空间。所以我们必须要解决这个问题。rune是我们的救星。

rune

rune是Go中的内置类型,它是int32的别名。在Go中,rune表示一个Unicode码点。无论一个码点会被编码为多少个字节,它都可以表示为一个rune。让我们修改上面的程序,使用rune来打印字符串中的字符。

packagemain import( "fmt" ) funcprintBytes(sstring){ fori:=0;i<len(s);i++{ fmt.Printf("%x",s[i]) } } funcprintChars(sstring){ runes:=[]rune(s) fori:=0;i<len(runes);i++{ fmt.Printf("%c",runes[i]) } } funcmain(){ name:="HelloWorld" printBytes(name) fmt.Printf("\n") printChars(name) fmt.Printf("\n\n") name="Señor" printBytes(name) fmt.Printf("\n") printChars(name) }123456789101112131415161718192021222324252627282930

在上面的程序中,第14行,字符串被转换为tune切片。然后我们遍历该切片并打印其中的字符。程序的输出如下:

48656c6c6f20576f726c64 HelloWorld 5365c3b16f72 Señor12345

上面的输出是正确的。这正是我们想要的结果。

使用rangefor遍历字符串

上面的程序是遍历字符串中字符的一个正确方式。但是Go提供了一种更简单的方式来做到这一点:使用rangefor。

packagemain import( "fmt" ) funcprintCharsAndBytes(sstring){ forindex,rune:=ranges{ fmt.Printf("%cstartsatbyte%d\n",rune,index) } } funcmain(){ name:="Señor" printCharsAndBytes(name) }12345678910111213141516

在上面的程序中,第8行通过使用rangefor遍历字符串。range返回一个rune(在byte数组中)的位置,以及rune本身。上面的程序输出为:

Sstartsatbyte0 estartsatbyte1 ñstartsatbyte2 ostartsatbyte4 rstartsatbyte512345

从上面的输出可以看到,ñ占两个字节:)

通过byte切片创建字符串

packagemain import( "fmt" ) funcmain(){ byteSlice:=[]byte{0x43,0x61,0x66,0xC3,0xA9} str:=string(byteSlice) fmt.Println(str) } 123456789101112

在上面的程序中,byteSlice是"Café"经过UTF-8编码后得到的切片(用16进制表示)。上面的程序输出为:Café。

如果我们换成对应的十进制数程序会正常工作吗?答案是:Yes。让我们测试一下:

packagemain import( "fmt" ) funcmain(){ byteSlice:=[]byte{67,97,102,195,169}//decimalequivalentof{\x43,\x61,\x66,\xC3,\xA9} str:=string(byteSlice) fmt.Println(str) }1234567891011

上面的程序同样输出:Café。

通过rune切片创建字符串

packagemain import( "fmt" ) funcmain(){ runeSlice:=[]rune{0x0053,0x0065,0x00f1,0x006f,0x0072} str:=string(runeSlice) fmt.Println(str) }1234567891011

在上面的程序中,runeSlice包含了字符串"Señor"的Unicode码点(以16进制表示)。程序的输出为:Señor。

字符串的长度

utf8包提供了funcRuneCountInString(sstring)(nint)来获取字符串的长度,该方法接受一个字符串作为参数,并返回该字符串中rune的数量。

(译者注:RuneCountInString返回字符串中Unicode字符的个数,而len返回字符串中byte的个数,注意两者的区别。)

packagemain import( "fmt" "unicode/utf8" ) funclength(sstring){ fmt.Printf("lengthof%sis%d\n",s,utf8.RuneCountInString(s)) } funcmain(){ word1:="Señor" length(word1) word2:="Pets" length(word2) }12345678910111213141516171819

上面程序的输出为:

lengthofSeñoris5 lengthofPetsis412

字符串是不可变的

在Go中字符串是不可变的。字符串一旦被创建就无法改变。

packagemain import( "fmt" ) funcmutate(sstring)string{ s[0]=a//anyvalidunicodecharacterwithinsinglequoteisarune returns } funcmain(){ h:="hello" fmt.Println(mutate(h)) }1234567891011121314

上面的程序中,第8行我们试图改变字符串的第一个字符为a。因为字符串是不可变的,因此这是非法的,将会报错:main.go:8:cannotassigntos[0]。

为了改变一个字符串中的字符,我们需要先把字符串转换为rune切片,然后修改切片中的内容,最后将这个切片转换回字符串。

packagemain import( "fmt" ) funcmutate(s[]rune)string{ s[0]=a returnstring(s) } funcmain(){ h:="hello" fmt.Println(mutate([]rune(h))) }1234567891011121314

在上面的程序中,第7行mutate函数接受一个rune切片作为参数。然后将该切片的第一个元素改为a,最后再转换回字符串并返回。该函数在程序中的第13行被调用。h被转换为一个rune切片传递给mutate。程序的输出为:aello。

原文链接: