Go语言 String 简介

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

什么是字符串?

字符串是Go 中的一段字节。可以通过将一组字符括在双引号内来创建字符串" "

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

让我们看一个创建 string并打印它的简单示例。

package main

import (  
    "fmt"
)

func main() {  
    name := "Hello World"
    fmt.Println(name)
}

Run in playground

上面的程序将打印Hello World.

访问String的各个字节

由于字符串是字节切片,因此可以访问字符串的每个字节。

package main

import (  
    "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)
}

Run in playground

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

**%x 是十六进制的格式说明符。**上述程序输出

String: Hello World  
Bytes: 48 65 6c 6c 6f 20 57 6f 72 6c 64  

这些是的[Unicode UT8 编码](https://mothereff.in/utf-8#Hello World)值Hello World

为了更好地理解字符串,需要对 Unicode 和 UTF-8 有基本的了解。

我建议阅读https://naveenr.net/unicode-character-set-and-utf-8-utf-16-utf-32-encoding/以了解有关 Unicode 和 UTF-8 的更多信息。

访问字符串的各个字符

让我们稍微修改一下上面的程序来打印字符串的字符。

package main

import (  
    "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)
}

Run in playground

在上面程序中,%c格式说明符用于printChars方法中打印字符串的字符。程序打印出

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  

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

package main

import (  
    "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)
}

Run in playground

上述程序的输出是

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,这是错误的。为什么这个程序在它工作得很好时会中断.原因是 的 Unicode 码位及其 UTF-8 编码占用 2 个字节我们尝试打印字符,假设每个代码点将是一个字节长,这是错误的。

在 UTF-8 编码中,一个码位可以占用 1 个以上的字节。那么我们如何解决这个问题呢?这就是Rune拯救我们的地方。

Rune

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

package main

import (  
    "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)
}

Run in playground

上面程序中,字符串被转换为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 main

import (  
    "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)
}

Run in playground

在上面程序的第 8 行中,使用for range循环迭代字符串。该程序输出

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

从上面的输出可以清楚地看出,它ñ占用 2 个字节,因为下一个字符o从字节 4 开始,而不是字节 3 。

从字节切片创建字符串

package main

import (  
    "fmt"
)

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

Run in playground

程序打印出

Café  

如果我们是十进制怎么办?上面的程序能运行吗?让我们来看看。

package main

import (  
    "fmt"
)

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

Run in playground

十进制值也可以工作,上面的程序也将打印Café

从rune片段创建字符串

package main

import (  
    "fmt"
)

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

Run in playground

上面的程序中包含了十六进制runeSlice字符串的 Unicode 代码点。程序输出

Señor  

字符串长度

utf8包RuneCountInString(s string) (n int)的函数可以用来求字符串的长度。该方法接受一个字符串作为参数并返回其中的rune数量。

正如我们之前讨论的, len(s)用于查找字符串中的字节数,但它不返回字符串长度。

正如我们已经讨论过的,某些 Unicode 字符的代码点占用的空间超过 1 个字节。使用len找出这些字符串的长度将返回不正确的字符串长度。

package main

import (  
    "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))
}

Run in playground

上述程序的输出是

String: Señor  
Length: 5  
Number of bytes: 6

String: Pets  
Length: 4  
Number of bytes: 4  

上面的输出证实了这一点len(s)RuneCountInString(s)返回不同的值。

字符串比较

==运算符用于比较两个字符串是否相等。如果两个字符串相等,则结果为true否则为false

package main

import (  
    "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)

}

Run in playground

compareStrings上面的函数中,使用运算符比较两个字符串str1str2是否相等==。如果它们相等,则打印相应的消息并且函数返回。

上面的程序打印出,

Go and Go are equal  
hello and world are not equal  

字符串连接

Go 中有多种执行字符串连接的方法。让我们看一下其中的几个。

执行字符串连接的最简单方法是使用+运算符。

package main

import (  
    "fmt"
)

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

Run in playground

该程序打印,

Go is awesome  

连接字符串的第二种方法是

使用fmt 包的Sprintf函数。

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

package main

import (  
    "fmt"
)

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

Run in playground

上面程序中,%s %s是 的格式说明符输入。此格式说明符采用两个字符串作为输入,并且中间有一个空格。这会将两个字符串连接起来,中间有一个空格。结果字符串存储在result. 该程序还打印,

Go is awesome  

字符串是不可变的

Go 中的字符串是不可变的。一旦创建了字符串,就无法更改它。

package main

import (  
    "fmt"
)

func mutate(s string)string {  
    s[0] = 'a'//any valid unicode character within single quote is a rune 
    return s
}
func main() {  
    h := "hello"
    fmt.Println(mutate(h))
}

Run in playground

上面程序的第8行,我们尝试将字符串的第一个字符改为'a'

单引号内的任何有效 Unicode 字符都是rune。

我们尝试将a分配到切片的第 0 个位置。这是不允许的,因为字符串是不可变的,因此程序无法编译并出现错误**./prog.go:8:7: 无法分配给 s[0]**

为了解决这个字符串不可变性,字符串被转换为rune切片。然后,该切片会根据需要进行任何更改,并转换回新字符串。

package main

import (  
    "fmt"
)

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

Run in playground

在上述程序中,该mutate函数接受rune切片作为参数。然后它将切片的第一个元素更改为'a',将rune转换回字符串并返回它。

该程序输出

aello

你可能感兴趣的:(Go语言教程,golang,开发语言,后端)