运维八一 运维八一
首页
运维杂记
编程浅尝
周积跬步
专栏
生活
关于
收藏
  • 分类
  • 标签
  • 归档
Source (opens new window)

运维八一

运维,运维!
首页
运维杂记
编程浅尝
周积跬步
专栏
生活
关于
收藏
  • 分类
  • 标签
  • 归档
Source (opens new window)
  • Go

    • 前言

    • Go基础知识

    • Go基本语法

    • 实战项目:简单web服务

    • 基本数据类型

      • 内置类型
      • 内置函数
      • 数字 int
      • 布尔值 bool
      • 字符串 string
        • 5.字符串 string
          • 5.1 go对字符串类型的支持
          • 5.1.1 Go 原生支持字符串的好处
          • 5.1.2 Go 字符串的组成
          • 5.2 字符串介绍
          • 5.3 多行字符串
          • 5.4 byte和rune
          • 5.5 字符串常用操作
          • len(str)
          • +(拼接)
          • strings.Split()
          • strings.Join()
          • 单引号
          • 下标操作
          • 字符串比较
          • 5.6 字符串遍历
          • 遍历字符串
          • 修改字符串
          • 5.7 转string
          • strconv
          • string与int转换
      • 数组 array
      • 切片 sice
      • 字典 map
      • 指针
    • 内置运算符

    • 分支和循环

    • 函数 function

    • 结构体 struct

    • 方法 method

    • 实战项目:跟踪函数调用链

    • 接口 interface

    • 并发 concurrency

    • 指针

    • 实战项目:实现轻量级线程池

    • 实战项目:实现TCP服务器

    • go常用包

    • Gin框架

    • go随记

  • Python

  • Shell

  • Java

  • Vue

  • 前端

  • 编程浅尝
  • Go
  • 基本数据类型
lyndon
2022-06-07
目录

字符串 string

# 5.字符串 string

# 5.1 go对字符串类型的支持

# 5.1.1 Go 原生支持字符串的好处

首先,对比C 语言,没有提供对字符串类型的原生支持,也就是说,C 语言中并没有“字符串”这个数据类型。在 C 语言中,字符串是以字符串字面值或以’\0’结尾的字符类型数组来呈现的,比如下面代码:

#define GO_SLOGAN "less is more"
const char * s1 = "hello, gopher"
char s2[] = "I love go"
1
2
3

这样定义的非原生字符串在使用过程中会有很多问题,比如:

  • 不是原生类型,编译器不会对它进行类型校验,导致类型安全性差;
  • 字符串操作时要时刻考虑结尾的’\0’,防止缓冲区溢出;
  • 以字符数组形式定义的“字符串”,它的值是可变的,在并发场景中需要考虑同步问题;
  • 获取一个字符串的长度代价较大,通常是 O(n) 时间复杂度;
  • C 语言没有内置对非 ASCII 字符(如中文字符)的支持。

C 代码换成等价的 Go 代码是这样的:

const (
  GO_SLOGAN = "less is more" // GO_SLOGAN是string类型常量
  s1 = "hello, gopher"       // s1是string类型常量
)

var s2 = "I love go" // s2是string类型变量
1
2
3
4
5
6

带来的好处:

==第一点:string 类型的数据是不可变的,提高了字符串的并发安全性和存储利用率。==

var s string = "hello"
s[0] = 'k'   // 错误:字符串的内容是不可改变的
s = "gopher" // ok
1
2
3

==第二点:没有结尾’\0’,而且获取长度的时间复杂度是常数时间,消除了获取字符串长度的开销。==

==第三点:原生支持“所见即所得”的原始字符串,大大降低构造多行字符串时的心智负担。==

// Go 语言原始字符串中的任意转义字符都不会起到转义的作用
var s string = `         ,_---~~~~~----._
    _,,_,*^____      _____*g*\"*,--,
   / __/ /'     ^.  /      \ ^@q   f
  [  @f | @))    |  | @))   l  0 _/
   \/   \~____ / __ \_____/     \
    |           _l__l_           I
    }          [______]           I
    ]            | | |            |
    ]             ~ ~             |
    |                            |
     |                           |`
fmt.Println(s)
1
2
3
4
5
6
7
8
9
10
11
12
13

==第四点:对非 ASCII 字符提供原生支持,消除了源码在不同环境下显示乱码的可能。==

# 5.1.2 Go 字符串的组成

字节视角

Go 语言中的字符串值也是一个可空的字节序列,字节序列中的字节个数称为该字符串的长度。一个个的字节只是孤立数据,不表意。

var s = "中国人"
fmt.Printf("the length of s = %d\n", len(s)) // 9

for i := 0; i < len(s); i++ {
  fmt.Printf("0x%x ", s[i]) // 0xe4 0xb8 0xad 0xe5 0x9b 0xbd 0xe4 0xba 0xba
}
fmt.Printf("\n")
1
2
3
4
5
6
7

字符视角

字符串是由一个可空的字符序列构成。

var s = "中国人"
fmt.Println("the character count in s is", utf8.RuneCountInString(s)) // 3

for _, c := range s {
  fmt.Printf("0x%x ", c) // 0x4e2d 0x56fd 0x4eba
}
fmt.Printf("\n")
1
2
3
4
5
6
7

# 5.2 字符串介绍

Go 语言里的字符串的内部实现使用 UTF-8 编码。字符串的值为双引号(“)中的内容,可以在 Go 语言的源码中直接添加非 ASCII 码字符。

s1 := "hello"
s2 := "你好"
1
2

string 类型其实是一个“描述符”,它本身并不真正存储字符串数据,而仅是由一个指向底层存储的指针和字符串的长度字段组成的。下图直观地展示了一个 string 类型变量在 Go 内存中的存储:

img

# 5.3 多行字符串

反引号(`)间换行将被作为字符串中的换行,但是所有的转义字符均无效,文本将会原样输出。

package main

import "fmt"

func main() {
   s1 := `
   第一行
   第二行
   第三行
   `
   fmt.Println(s1)
}

输出:
        第一行
        第二行
        第三行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 5.4 byte和rune

Go 语言的字符有以下两种:

  1. uint8类型,或者叫 byte 型:代表了ASCII码的一个字符。
  2. rune类型:代表一个 UTF-8字符。

字符串底层是一个byte数组,所以可以和[]byte类型相互转换;

字符串是不能修改的;

字符串是由byte字节组成,所以字符串的长度是byte字节的长度;

rune类型用来表示utf8字符,一个rune字符由一个或多个byte组成。

package main

import "fmt"

func main() {
	s := "川普总统"
	s_rune := []rune(s)
	fmt.Println("拜登" + string(s_rune)[:2])
}

输出:
拜登总统
1
2
3
4
5
6
7
8
9
10
11
12

# 5.5 字符串常用操作

image-20220327003120484

# len(str)

package main

import "fmt"

func main() {
   var str = "this is str"
   fmt.Println(len(str))	// 11
}
1
2
3
4
5
6
7
8

# +(拼接)

package main

import "fmt"

func main() {
   var str1 = "你好"
   var str2 = "golang"
   fmt.Println(str1 + str2)		// 你好golang
}
1
2
3
4
5
6
7
8
9

除了这个方法外,Go 还提供了 strings.Builder、strings.Join、fmt.Sprintf 等函数来进行字符串连接操作。

# strings.Split()

package main

import (
   "fmt"
   "strings"
)

func main() {
   var s = "123-456-789"
   var arr = strings.Split(s, "-")
   fmt.Println(arr)		// [123 456 789]
}
1
2
3
4
5
6
7
8
9
10
11
12

# strings.Join()

package main

import (
   "fmt"
   "strings"
)

func main() {
   var str = "123-456-789"
   var arr = strings.Split(str, "-")	// [123 456 789]
   var str2 = strings.Join(arr, "*")	// 123*456*789
   fmt.Println(arr)
   fmt.Println(str2)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 单引号

组成每个字符串的元素叫做“字符”,可以通过遍历字符串元素获得字符,字符用单引号(’)表示。

package main

import "fmt"

func main() {
   a := 'a'
   name := "zhangsan"
   //当我们直接输出 byte(字符)的时候输出的是这个字符对应的码值
   fmt.Println(a)    	// 97 这里输出的是 a 字符串的 ASCII值
   fmt.Println(name) 	// zhangsan
   //如果我们要输出这个字符,需要格式化输出
   fmt.Printf("值是%c", a) 	// 值是a
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 下标操作

在字符串的实现中,真正存储数据的是底层的数组。字符串的下标操作本质上等价于底层数组的下标操作。

var s = "中国人"
fmt.Printf("0x%x\n", s[0]) // 0xe4:字符“中” utf-8编码的第一个字节
1
2

通过下标操作,获取的是字符串中特定下标上的字节,而不是字符。

# 字符串比较

Go 字符串类型支持各种比较关系操作符,包括 = =、!= 、>=、<=、> 和 <。

func main() {
        // ==
        s1 := "世界和平"
        s2 := "世界" + "和平"
        fmt.Println(s1 == s2) // true

        // !=
        s1 = "Go"
        s2 = "C"
        fmt.Println(s1 != s2) // true
    
        // < and <=
        s1 = "12345"
        s2 = "23456"
        fmt.Println(s1 < s2)  // true
        fmt.Println(s1 <= s2) // true
    
        // > and >=
        s1 = "12345"
        s2 = "123"
        fmt.Println(s1 > s2)  // true
        fmt.Println(s1 >= s2) // true

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 5.6 字符串遍历

# 遍历字符串

package main

import "fmt"

func main() {
   s := "hello 张三"
   // 方法一:for遍历
   for i := 0; i < len(s); i++ {
      fmt.Printf("%v(%c)", s[i], s[i])
      // 104(h) 101(e) 108(l) 108(l) 111(o) 32( ) 229(å) 188(¼) 160() 228(ä) 184(¸) 137()
   }
   fmt.Println() // 打印一个换行

   //方法二:for range遍历
   for _, r := range s { //rune
      fmt.Printf("%v==>%c", r, r)
      // 104=>h 101=>e 108=>l 108=>l 111=>o 32=> 24352=>张 19977=>三
      fmt.Println()
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Go 有两种迭代形式:==常规 for 迭代与 for range 迭代。==

  • 通过常规 for 迭代对字符串进行的操作是一种字节视角的迭代,每轮迭代得到的的结果都是组成字符串内容的一个字节,以及该字节所在的下标值,这也等价于对字符串底层数组的迭代。
  • 通过 for range 迭代,我们每轮迭代得到的是字符串中 Unicode 字符的码点值,以及该字符在字符串中的偏移值。

# 修改字符串

要修改字符串,需要先将其转换成[]rune 或[]byte,完成后再转换为 string。无论哪种转换,都会重新分配内存,并复制字节数组。

将“美国第一”改成“中国第一”

package main

import "fmt"

func main() {
   s := "美国第一"
   s_rune := []rune(s)
   fmt.Println("中国" + string(s_rune[2:])) //中国第一
}
1
2
3
4
5
6
7
8
9

# 5.7 转string

# strconv

package main

import (
   "fmt"
   "strconv"
)

func main() {
   //1、int 转换成 string
   var num1 int = 20
   s1 := strconv.Itoa(num1)
   fmt.Printf("类型: %T ,值=%v \n", s1, s1) // 类型: string ,值=20
   // 2、float 转 string
   var num2 float64 = 20.113123
   /* 参数 1:要转换的值
      参数 2:格式化类型
      参数 3: 保留的小数点 -1(不对小数点格式化)
      参数 4:格式化的类型
   */
   s2 := strconv.FormatFloat(num2, 'f', 2, 64)
   fmt.Printf("类型: %T ,值=%v \n", s2, s2) // 类型: string ,值=20.11
   // 3、bool 转 string
   s3 := strconv.FormatBool(true)
   fmt.Printf("类型: %T ,值=%v \n", s3, s3) // 类型: string ,值=20.11
   //4、int64 转 string
   var num3 int64 = 20
   s4 := strconv.FormatInt(num3, 10)    /* 第二个参数10为 进制 */
   fmt.Printf("类型: %T ,值=%v \n", s4, s4) // 类型 string ,值=20
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# string与int转换

package main

import (
   "fmt"
   "strconv"
)

func main() {
   //1、int string 互转
   num := 100
   strNum := strconv.Itoa(num)
   fmt.Printf("num: %T %v \n", num, num)
   fmt.Printf("strNum: %T %v \n", strNum, strNum)

   intNum, _ := strconv.Atoi(strNum)
   fmt.Printf("intNum: %T %v \n", intNum, intNum)

}

/*
num: int 100
strNum: string 100
intNum: int 100
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Go 还支持字符串与字节切片、字符串与 rune 切片的双向转换,并且这种转换无需调用任何函数,只需使用显式类型转换就可以。

var s string = "中国人"
                      
// string -> []rune
rs := []rune(s) 
fmt.Printf("%x\n", rs) // [4e2d 56fd 4eba]
             
// string -> []byte
bs := []byte(s) 
fmt.Printf("%x\n", bs) // e4b8ade59bbde4baba
                
// []rune -> string
s1 := string(rs)
fmt.Println(s1) // 中国人
                
// []byte -> string
s2 := string(bs)
fmt.Println(s2) // 中国人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
上次更新: 2022/06/12, 15:48:09
布尔值 bool
数组 array

← 布尔值 bool 数组 array→

最近更新
01
ctr和crictl显示镜像不一致
03-13
02
alpine镜像集成常用数据库客户端
03-13
03
create-cluster
02-26
更多文章>
Theme by Vdoing | Copyright © 2015-2024 op81.com
苏ICP备18041258号-2
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式