1、什么是字符串?

Go语言中字符串是一个字节切片。把内容放在双引号""之间,我们可以创建一个字符串,让我们来看一下创建并打印字符串的简单示例。

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。
package main

import (
    "fmt"
)

func main() {
    str := "hello golang"
    fmt.Println(str)
}
在线运行程序
hello golang

2、获取字符串的每一个字节

由于字符串是一个字节切片,所以我们可以获取字符串的每一个字节

package main

import (
    "fmt"
)

func main() {
    str := "hello golang"
    printChars(str)
    fmt.Println()
    printBytes(str)
    fmt.Println()
    printCharsAndBytes(str)
}

// 字节 %x 限定打印字符串字节
func printBytes(s string) {
    for i := 0; i < len(s); i++ {
        fmt.Printf("%x ", s[i])
    }
}

// 字符  %c 限定打印字符串的字符
func printChars(s string) {
    for i := 0; i < len(s); i++ {
        fmt.Printf("%c  ", s[i])
    }
}

// for range 循环是最简单方法方
func printCharsAndBytes(s string) {
    for _, rune := range s {
        fmt.Printf("%c starts at byte %x\n", rune, rune)
    }
}
在线运行程序
h  e  l  l  o     g  o  l  a  n  g  
68 65 6c 6c 6f 20 67 6f 6c 61 6e 67 
h starts at byte 0
e starts at byte 1
l starts at byte 2
l starts at byte 3
o starts at byte 4
  starts at byte 5
g starts at byte 6
o starts at byte 7
l starts at byte 8
a starts at byte 9
n starts at byte a
g starts at byte b
airdeMacBook-Air:learn_demo wutianxiang$ go run main.go 
h  e  l  l  o     g  o  l  a  n  g  
68 65 6c 6c 6f 20 67 6f 6c 61 6e 67 
h starts at byte 68
e starts at byte 65
l starts at byte 6c
l starts at byte 6c
o starts at byte 6f
  starts at byte 20
g starts at byte 67
o starts at byte 6f
l starts at byte 6c
a starts at byte 61
n starts at byte 6e
g starts at byte 67

如果我们再上面基础上字符串含中文,那会如何?

package main

import (
    "fmt"
)

func main() {
    str := "hello golang 语言"
    printChars(str)
    fmt.Println()
    printBytes(str)
    fmt.Println()
    printCharsAndBytes(str)
}

// 字节 %x 限定打印字符串字节
func printBytes(s string) {
    for i := 0; i < len(s); i++ {
        fmt.Printf("%x ", s[i])
    }
}

// 字符  %c 限定打印字符串的字符
func printChars(s string) {
    for i := 0; i < len(s); i++ {
        fmt.Printf("%c  ", s[i])
    }
}

// for range 循环是最简单方法方
func printCharsAndBytes(s string) {
    for _, rune := range s {
        fmt.Printf("%c starts at byte %x\n", rune, rune)
    }
}
在线运行程序
h  e  l  l  o     g  o  l  a  n  g     è  ¯  ­  è  ¨    
68 65 6c 6c 6f 20 67 6f 6c 61 6e 67 20 e8 af ad e8 a8 80 
h starts at byte 68
e starts at byte 65
l starts at byte 6c
l starts at byte 6c
o starts at byte 6f
  starts at byte 20
g starts at byte 67
o starts at byte 6f
l starts at byte 6c
a starts at byte 61
n starts at byte 6e
g starts at byte 67
  starts at byte 20
语 starts at byte 8bed
言 starts at byte 8a00

上面第一行输出了错误 h e l l o g o l a n g è ¯ ­ è ¨ 

为什么程序分割 hello golang 时表现完美,但分割字符含中文就错误?

这是“语言”的unicode代码点(code point)是U+00F1。他的UTF-8编码占用了 e8 af ad e8 a8 80

6个字节。

它的UTF-8编码占用了8个字节 e8 af ad e8 a8 80。而我们打印字符时,却假定每个字符的编码只会占用一个字节,这是错误的。

在UTF-8编码中,一个代码点可能会占用超过一个字节的空间。

那么我们该怎么办呢?

rune 能帮我们解决这个难题。

rune是Go语言的内建类型,它是int32的别称。在Go语言中,rune表示一个代码点,无论代码点占用多少个字节,都可以用一个rune来表示

package main

import (
    "fmt"
)

func main() {
    str := "hello golang 语言"
    printChars(str)
    fmt.Println()
    printBytes(str)
    fmt.Println()
    printCharsAndBytes(str)
}

// 字节 %x 限定打印字符串字节
func printBytes(s string) {
    for i := 0; i < len(s); i++ {
        fmt.Printf("%x ", s[i])
    }
}

// 字符  %c 限定打印字符串的字符
func printChars(s string) {
    runes := []rune(s)
    for i := 0; i < len(runes); i++ {
        fmt.Printf("%c  ", runes[i])
    }
}

// for range 循环是最简单方法方
func printCharsAndBytes(s string) {
    for _, rune := range s {
        fmt.Printf("%c starts at byte %x\n", rune, rune)
    }
}
在线运行程序
h  e  l  l  o     g  o  l  a  n  g     语  言  
68 65 6c 6c 6f 20 67 6f 6c 61 6e 67 20 e8 af ad e8 a8 80 
h starts at byte 68
e starts at byte 65
l starts at byte 6c
l starts at byte 6c
o starts at byte 6f
  starts at byte 20
g starts at byte 67
o starts at byte 6f
l starts at byte 6c
a starts at byte 61
n starts at byte 6e
g starts at byte 67
  starts at byte 20
语 starts at byte 8bed
言 starts at byte 8a00

3、用字节切片构造字符串

package main

import (
    "fmt"
)

func main() {
    // 十六进制
    byteSlice := []byte{0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x67, 0x6F, 0x6C, 0x61, 0x6E, 0x67, 0x20, 0xE8, 0xAF, 0xAD, 0xE8, 0xA8, 0x80}
    str := string(byteSlice)
    fmt.Println(str)
}
在线运行程序
hello golang 语言

4、rune 切片构造字符串

package main

import (
    "fmt"
)

func main() {
    runeSlice := []rune{104, 101, 108, 108, 111, 32, 103, 111, 108, 97, 110, 103, 32, 35821, 35328}
    str := string(runeSlice)
    fmt.Println(str)
}
在线运行程序
hello golang 语言
5、获取字符串长度
对于字母数字组合成字符串获取长度,用len()方法;
对于中文或其它国家文字组成字符串获取长度,用utf8.RuneCountInString()方法
package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    str1 := "hello"
    length(str1)
    str2 := "hello您好"
    length(str2)
}

// 获取字符串长度
func length(s string) {
    fmt.Printf("length of %s is %d\n", s, utf8.RuneCountInString(s))
}

6、字符串是不可变的。一旦创建一个字符串,那么他将无法被修改。如果试图把字符串修改,程序会抛出一个错误:cannot assign to s[0]

package main

import (
    "fmt"
)

func main() {
    str := "hello"
    fmt.Println(midfyString(str))
}

// 修改字符串
func midfyString(s string) string {
    s[0] = 'a'
    return s
}

上面这个操作是非法的。

那么如果修改字符,可以把字符串转化一个rune切片,然后对这个切片任何修改,再将转化一个新的字符串

package main

import (
    "fmt"
)

func main() {
    str := "hello"
    fmt.Println(midfyString(str))
    fmt.Println(str)
}

// 修改字符串
func midfyString(s string) string {
    runes := []rune(s)
    // 注意这个是单引号。如果改成双引号,可以试试发生什么
    runes[0] = 'a'
    return string(runes)
}

7、字符串拼接,后续。。

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄