龙空技术网

Go中string与其它语言有什么不同?

驯鹿的古牧 487

前言:

此刻同学们对“c语言中string是什么”大体比较看重,同学们都想要知道一些“c语言中string是什么”的相关知识。那么小编也在网摘上汇集了一些对于“c语言中string是什么””的相关内容,希望小伙伴们能喜欢,姐妹们快快来了解一下吧!

字符串在 Go 中值得特别提及,因为与其他语言相比,它们在实现上是不同的。

什么是string?

字符串string在Go中是一个bytes类型的切片。通过双引号创建字符串string。

name := "Hello World"

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

访问字符串的单个字节

由于字符串是bytes类型的切片,所以完全可以访问字符串的每个字节。

package mainimport (      "fmt")func printBytes(s string) {      fmt.Printf("Bytes: ")    for i := 0; i < len(s); i++ {        fmt.Printf("%x ", s[i])    }}func main() {      name := "Hello World"    fmt.Printf("String: %s\n", name)    printBytes(name)}

%s 是打印字符串的格式说明符。在第16行中,打印输入字符串。第9行中,len(s)返回字符串中的字节数,我们使用 for 循环以十六进制表示法打印这些字节。%x 是十六进制的格式说明符。以上程序输出,

String: Hello World  Bytes: 48 65 6c 6c 6f 20 57 6f 72 6c 64 
访问字符串的单个字符

让我们简单修改上面的程序来打印字符串的字符。

package mainimport (      "fmt")func printBytes(s string) {      fmt.Printf("Bytes: ")    for i := 0; i < len(s); i++ {        fmt.Printf("%x ", s[i])    }}func printChars(s string) {      fmt.Printf("Characters: ")    for i := 0; i < len(s); i++ {        fmt.Printf("%c ", s[i])    }}func main() {      name := "Hello World"    fmt.Printf("String: %s\n", name)    printChars(name)    fmt.Printf("\n")    printBytes(name)}

上面程序中的第17行,printChars函数%c格式符用来打印字符串中的字符。会有如下打印:

String: Hello World  Characters: H e l l o   W o r l d  Bytes: 48 65 6c 6c 6f 20 57 6f 72 6c 64  

尽管上述程序看起来像是访问字符串各个字符的合法方法,但这有一个严重的bug。让我们找出该bug是什么。

package mainimport (      "fmt")func printBytes(s string) {      fmt.Printf("Bytes: ")    for i := 0; i < len(s); i++ {        fmt.Printf("%x ", s[i])    }}func printChars(s string) {      fmt.Printf("Characters: ")    for i := 0; i < len(s); i++ {        fmt.Printf("%c ", s[i])    }}func main() {      name := "Hello World"    fmt.Printf("String: %s\n", name)    printChars(name)    fmt.Printf("\n")    printBytes(name)    fmt.Printf("\n\n")    name = "Señor"    fmt.Printf("String: %s\n", name)    printChars(name)    fmt.Printf("\n")    printBytes(name)}

上面的程序输出,

String: Hello World  Characters: H e l l o   W o r l d  Bytes: 48 65 6c 6c 6f 20 57 6f 72 6c 64 String: Señor  Characters: S e à ± o r  Bytes: 53 65 c3 b1 6f 72 

在上面程序的第30行中,我们尝试打印Señor的字符,它输出S e à ± o r这是错误的。为什么这个程序在Señor中中断,而它对 Hello World 来说效果很好。原因是ñ的 Unicode 码位是 U+00F1,其 UTF-8编码占用2个字节c3 和b1。我们尝试打印字符,假设每个代码点将是一个字节长,这是错误的。在 UTF-8编码中,一个码位可以占用1个以上的字节。那么我们如何解决这个问题呢?这就是Rune拯救我们的地方。

Rune

rune是Go中的内置类型,它是int32的别名。Rune表示Go中的 Unicode 码位。无论代码点占用多少字节,它都可以用rune表示。让我们修改上面的程序以使用rune打印字符,

package mainimport (      "fmt")func printBytes(s string) {      fmt.Printf("Bytes: ")    for i := 0; i < len(s); i++ {        fmt.Printf("%x ", s[i])    }}func printChars(s string) {      fmt.Printf("Characters: ")    runes := []rune(s)    for i := 0; i < len(runes); i++ {        fmt.Printf("%c ", runes[i])    }}func main() {      name := "Hello World"    fmt.Printf("String: %s\n", name)    printChars(name)    fmt.Printf("\n")    printBytes(name)    fmt.Printf("\n\n")    name = "Señor"    fmt.Printf("String: %s\n", name)    printChars(name)    fmt.Printf("\n")    printBytes(name)}

在上面程序的第16行中,字符串被转换为一rune切片。然后我们循环它并显示字符。该程序打印,

String: Hello World  Characters: H e l l o   W o r l d  Bytes: 48 65 6c 6c 6f 20 57 6f 72 6c 64 String: Señor  Characters: S e ñ o r  Bytes: 53 65 c3 b1 6f 72 
使用for range循环访问rune

上面的程序是迭代字符串的各个rune的完美方法。但是 Go 为我们提供了一种更简单的方法来使用 for range循环来做到这一点。

package mainimport (      "fmt")func charsAndBytePosition(s string) {      for index, rune := range s {        fmt.Printf("%c starts at byte %d\n", rune, index)    }}func main() {      name := "Señor"    charsAndBytePosition(name)}

在上面程序的第8行中,字符串使用 for range 循环进行迭代。循环返回rune与rune一起开始的字节的位置。此程序输出,

S starts at byte 0  e starts at byte 1  ñ starts at byte 2o starts at byte 4  r starts at byte 5

从上面的例子可以看出,ñ占两个字节,由于下一个字符o从byte 4开始而不是3。

从一个bytes切片创建字符串

package mainimport (      "fmt")func main() {      byteSlice := []byte{0x43, 0x61, 0x66, 0xC3, 0xA9}    str := string(byteSlice)    fmt.Println(str)}

上面程序第8行中的 byteSlice 包含字符串Café的UTF-8 编码十六进制字节。程序打印,

Café 

如果我们有十六进制值的十进制等效值会怎么样?上述程序会起作用吗?一起来看看吧。

package mainimport (      "fmt")func main() {      byteSlice := []byte{67, 97, 102, 195, 169}// {'\x43', '\x61', '\x66', '\xC3', '\xA9'}    str := string(byteSlice)    fmt.Println(str)}

十进制值也有效,上面的程序也将打印Café。

从rules切片创建字符串

package mainimport (      "fmt")func main() {      runeSlice := []rune{0x0053, 0x0065, 0x00f1, 0x006f, 0x0072}    str := string(runeSlice)    fmt.Println(str)}

在上面的程序中,runeSlice 包含十六进制字符串 Señor 的 Unicode 码位。程序输出,

Señor  
字符串的长度

utf8包的RuneCountInString(s string) (n int) 函数可用于查找字符串的长度。此方法将字符串作为参数,并返回其中的runes数。

len(s)用于查找字符串中的字节数,并且不返回字符串长度。正如我们已经讨论过的,某些 Unicode 字符的代码点占用超过1个字节。使用 len 找出这些字符串的长度将返回不正确的字符串长度。

package mainimport (      "fmt"    "unicode/utf8")func main() {      word1 := "Señor"    fmt.Printf("String: %s\n", word1)    fmt.Printf("Length: %d\n", utf8.RuneCountInString(word1))    fmt.Printf("Number of bytes: %d\n", len(word1))    fmt.Printf("\n")    word2 := "Pets"    fmt.Printf("String: %s\n", word2)    fmt.Printf("Length: %d\n", utf8.RuneCountInString(word2))    fmt.Printf("Number of bytes: %d\n", len(word2))}

上面的程序输出,

String: Señor  Length: 5  Number of bytes: 6String: Pets  Length: 4  Number of bytes: 4  
字符串比较

==操作符常用于比较两个字符串是否相等。

package mainimport (      "fmt")func compareStrings(str1 string, str2 string) {      if str1 == str2 {        fmt.Printf("%s and %s are equal\n", str1, str2)        return    }    fmt.Printf("%s and %s are not equal\n", str1, str2)}func main() {      string1 := "Go"    string2 := "Go"    compareStrings(string1, string2)    string3 := "hello"    string4 := "world"    compareStrings(string3, string4)}

在上面的 compareString 函数中,第8行使用 == 运算符比较两个字符串 str1 和 str2 是否相等。如果它们相等,则打印相应的消息,函数返回。上面的程序打印,

Go and Go are equal  hello and world are not equal  
字符串拼接

Go中有多种方式进行字符串的拼接,让我们一一看一下。

最简单的方式用来拼接字符串是+运算符。

package mainimport (      "fmt")func main() {      string1 := "Go"    string2 := "is awesome"    result := string1 + " " + string2    fmt.Println(result)}

上面的程序中,string1和string2中间隔个空字符进行拼接,打印,

Go is awesome  

第二种方式是用fmt包中的Spintf函数。

Sprintf 函数根据输入格式说明符设置字符串的格式,并返回结果字符串。让我们使用 Sprintf 函数重写上面的程序。

package mainimport (      "fmt")func main() {      string1 := "Go"    string2 := "is awesome"    result := fmt.Sprintf("%s %s", string1, string2)    fmt.Println(result)}

在上面程序的第10行中,%s %s 是 Sprintf 的格式说明符输入。此格式说明符将两个字符串作为输入,并在两者之间有一个空格。这将连接两个字符串,中间有一个空格。生成的字符串存储在result中。该程序还打印,

Go is awesome  
字符串是不可变的

Go中字符串是不可变的,一旦字符串被被创建就不能修改它了。

package mainimport (      "fmt")func mutate(s string)string {      s[0] = 'a'    return s}func main() {      h := "hello"    fmt.Println(mutate(h))}

在上述程序的第8行中,我们尝试将字符串的第一个字符更改为'a'。单引号内的任何有效 Unicode字符都是rune。我们尝试将rune a 分配给切片的第0个位置。这是不允许的,因为字符串是不可变的,因此程序无法编译并显示错误./prog.go:8:7:cannot assign s[0]

为了解决这种字符串不可变性的问题,字符串被转换为rune切片。然后,该切片会随着所需的任何更改而发生变异,并转换回新字符串。

package mainimport (      "fmt")func mutate(s []rune) string {      s[0] = 'a'     return string(s)}func main() {      h := "hello"    fmt.Println(mutate([]rune(h)))}

在上述程序的第7行中,mutate函数接受rune切片作为参数。然后,它将切片的第一个元素更改为'a',将rune转换回字符串并返回它。此方法从程序的第13行调用。h被转换成一rune切片,并在第13行中传递到mutate。此程序输出aello。

总结

1.%s 是打印字符串的格式说明符;

%x是十六进制的格式说明符;

%c是字符格式说明符;由于UTF-8 编码中,一个代码点可以占用1个以上的字节,所以使用%c打印字符会出现bug,这时可以使用rune进行解决。

2. 使用for index, rune := range s 进行遍历rune字符;

3.len(s)用于查找字符串中的字节数;utf8包的RuneCountInString(s string) (n int) 函数可用于查找字符串的长度;

4.==操作符常用于比较两个字符串是否相等;

5.两种方式拼接字符串;

6.字符串是不可变的。

标签: #c语言中string是什么