Go 入门指南笔记 - 基本结构和基本数据类型 - 4

strings 和 strconv 包

Go 中使用 strings 包来完成对字符串的主要操作。

  • HasPrefix 判断字符串 s 是否以 prefix 开头
  • HasSuffix 判断字符串 s 是否以 suffix 结尾
  • Contains 判断字符串 s 是否包含 substr
  • Index 返回字符串 str 在字符串 s 中的索引(str 的第一个字符的索引),-1 表示字符串 s 不包含字符串 str
  • LastIndex 返回字符串 str 在字符串 s 中最后出现位置的索引(str 的第一个字符的索引),-1 表示字符串 s 不包含字符串 str
  • strings.IndexRune(s string, ch int) int
  • Replace 用于将字符串 str 中的前 n 个字符串 old 替换为字符串 new,并返回一个新的字符串,如果 n = -1 则替换所有字符串 old 为字符串 new
  • Count 用于计算字符串 str 在字符串 s 中出现的非重叠次数
  • Repeat 用于重复 count 次字符串 s 并返回一个新的字符串
  • ToLower 将字符串中的 Unicode 字符全部转换为相应的小写字符
  • ToUpper 将字符串中的 Unicode 字符全部转换为相应的大写字符
  • strings.TrimSpace(s) 来剔除字符串开头和结尾的空白符号
  • 剔除指定字符,则可以使用 strings.Trim(s, "cut") 来将开头和结尾的 cut 去除掉
  • TrimLeftTrimRight 剔除开头或者结尾的字符串
  • strings.Fields(s) 将会利用 1 个或多个空白符号来作为动态长度的分隔符将字符串分割成若干小块,并返回一个 slice,如果字符串只包含空白符号,则返回一个长度为 0 的 slice
  • strings.Split(s, sep) 用于自定义分割符号来对指定字符串进行分割,同样返回 slice
  • Join 用于将元素类型为 string 的 slice 使用分割符号来拼接组成一个字符串
  • strings.NewReader(str) 用于生成一个 Reader 并读取字符串中的内容,然后返回指向该 Reader 的指针
    • Read() 从 []byte 中读取内容
    • ReadByte()ReadRune() 从字符串中读取下一个 byte 或者 rune
package main

import (
    "fmt"
    "strings"
)

func main() {
    var str string = "This is an example of a string"

    fmt.Printf("%t\n", strings.HasPrefix(str, "Th"))
    fmt.Printf("%d\n", strings.Index(str, "Burger"))
    // strings.Replace(str, old, new, n) string
    fmt.Printf("%d\n", strings.Count(str, "H"))

    var origS string = "Hi there! "
    var newS string
    newS = strings.Repeat(origS, 3)
    fmt.Printf("The new repeated string is: %s\n", newS)

    var orig string = "Hey, how are you George?"
    var lower string
    var upper string
    lower = strings.ToLower(orig)
    upper = strings.ToUpper(orig)

    str := "The quick brown fox jumps over the lazy dog"
    sl := strings.Fields(str)
    for _, val := range sl {
        fmt.Printf("%s - ", val)
    }
    fmt.Println()
    str3 := strings.Join(sl2,";")
    fmt.Printf("sl2 joined by ;: %s\n", str3)
}

与字符串相关的类型转换都是通过 strconv 包实现的。

  • 该包包含了一些变量用于获取程序运行的操作系统平台下 int 类型所占的位数,如:strconv.IntSize
  • 任何类型 T 转换为字符串总是成功的
  • 针对从数字类型转换到字符串,Go 提供了以下函数:
    • strconv.Itoa(i int) string 返回数字 i 所表示的字符串类型的十进制数
    • strconv.FormatFloat(f float64, fmt byte, prec int, bitSize int) string 将 64 位浮点型的数字转换为字符串,其中 fmt 表示格式(其值可以是 ‘b’、’e’、’f’ 或 ‘g’),prec 表示精度,bitSize 则使用 32 表示 float32,用 64 表示 float64
  • 将字符串转换为其它类型 tp 并不总是可能的,可能会在运行时抛出错误 parsing "…": invalid argument
  • 针对从字符串类型转换为数字类型,Go 提供了以下函数:
    • strconv.Atoi(s string) (i int, err error) 将字符串转换为 int 型
    • strconv.ParseFloat(s string, bitSize int) (f float64, err error) 将字符串转换为 float64 型
// 利用多返回值的特性,这些函数会返回 2 个值,第 1 个是转换后的结果(如果转换成功),第 2 个是可能出现的错误
val, err = strconv.Atoi(s)
package main

import (
    "fmt"
    "strconv"
)

func main() {
    var orig string = "666"
    var an int
    var newS string

    fmt.Printf("The size of ints is: %d\n", strconv.IntSize)

    an, _ = strconv.Atoi(orig)
    fmt.Printf("The integer is: %d\n", an)
    an = an + 5
    newS = strconv.Itoa(an)
    fmt.Printf("The new string is: %s\n", newS)
}

时间和日期

time 包为我们提供了一个数据类型 time.Time(作为值使用)以及显示和测量时间和日期的功能函数。

  • 当前时间可以使用 time.Now() 获取,或者使用 t.Day()、t.Minute() 等等来获取时间的一部分
  • 可以自定义时间格式化字符串,例如:fmt.Printf("%02d.%02d.%4d\n", t.Day(), t.Month(), t.Year())
  • Duration 类型表示两个连续时刻所相差的纳秒数,类型为 int64
  • Location 类型映射某个时区的时间,UTC 表示通用协调世界时间。
  • func (t Time) Format(layout string) string 可以根据一个格式化字符串来将一个时间 t 转换为相应格式的字符串,你可以使用一些预定义的格式,如:time.ANSIC 或 time.RFC822
  • 如果你需要在应用程序在经过一定时间或周期执行某项任务(事件处理的特例),则可以使用 time.After 或者 time.Ticker
  • time.Sleep(Duration d) 可以实现对某个进程(实质上是 goroutine)时长为 d 的暂停。
package main
import (
    "fmt"
    "time"
)

var week time.Duration
func main() {
    t := time.Now()
    fmt.Println(t) // e.g. Wed Dec 21 09:52:14 +0100 RST 2011
    fmt.Printf("%02d.%02d.%4d\n", t.Day(), t.Month(), t.Year())
    // 21.12.2011
    t = time.Now().UTC()
    fmt.Println(t) // Wed Dec 21 08:52:14 +0000 UTC 2011
    fmt.Println(time.Now()) // Wed Dec 21 09:52:14 +0100 RST 2011
    // calculating times:
    week = 60 * 60 * 24 * 7 * 1e9 // must be in nanosec
    week_from_now := t.Add(week)
    fmt.Println(week_from_now) // Wed Dec 28 08:52:14 +0000 UTC 2011
    // formatting times:
    fmt.Println(t.Format(time.RFC822)) // 21 Dec 11 0852 UTC
    fmt.Println(t.Format(time.ANSIC)) // Wed Dec 21 08:56:34 2011
    fmt.Println(t.Format("02 Jan 2006 15:04")) // 21 Dec 2011 08:52
    s := t.Format("20060102")
    fmt.Println(t, "=>", s)
    // Wed Dec 21 08:52:14 +0000 UTC 2011 => 20111221
}

指针

程序在内存中存储它的值,每个内存块(或字)有一个地址,通常用十六进制数表示,如:0x6b0820 或0xf84001d7f0

  • 你不能进行指针运算,但是 Go 允许你控制特定集合的数据结构、分配的数量以及内存访问模式
  • 指针对于性能的影响是不言而喻的,而如果你想要做的是系统编程、操作系统或者网络应用,指针更是不可或缺的一部分
  • Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。这个地址可以存储在一个叫做指针的特殊数据类型中。
  • 一个指针变量可以指向任何一个值的内存地址 它指向那个值的内存地址,在 32 位机器上占用 4 个字节,在 64 位机器上占用 8 个字节,并且与它所指向的值的大小无关
  • 可以声明指针指向任何类型的值来表明它的原始性或结构性
  • 在指针类型前面加上 * 号(前缀)来获取指针所指向的内容
    • 这里的 * 号是一个类型更改器
    • 使用一个指针引用一个值被称为间接引用或反引用。
    • 这个 * 号这里叫做反引用操作符,另一种说法叫指针转移
  • 当一个指针被定义后没有分配到任何变量时,它的值为 nil
  • 一个指针变量通常缩写为 ptr
  • 对于任何一个变量 var, 如下表达式都是正确的:var == *(&var)
package main
import "fmt"
func main() {
    var i1 = 5
    fmt.Printf("An integer: %d, its location in memory: %p\n", i1, &i1)
    var intP *int
    intP = &i1
    fmt.Printf("The value at memory location %p is %d\n", intP, *intP)
}
package main
import "fmt"
func main() {
    s := "good bye"
    var p *string = &s
    *p = "ciao"
    fmt.Printf("Here is the pointer p: %p\n", p) // prints address
    fmt.Printf("Here is the string *p: %s\n", *p) // prints string
    fmt.Printf("Here is the string s: %s\n", s) // prints same string
}

输出

Here is the pointer p: 0x2540820
Here is the string *p: ciao
Here is the string s: ciao

通过对 *p 赋另一个值来更改“对象”,这样 s 也会随之更改。

你不能得到一个文字或常量的地址:

const i = 5
ptr := &i //error: cannot take the address of i
ptr2 := &10 //error: cannot take the address of 10

用法

  • 指针的一个高级应用是你可以传递一个变量的引用(如函数的参数),这样不会传递变量的拷贝。
  • 指针传递是很廉价的,只占用 4 个或 8 个字节。
  • 当程序在工作中需要占用大量的内存,或很多变量,或者两者都有,使用指针会减少内存占用和提高效率。
  • 被指向的变量也保存在内存中,直到没有任何指针指向它们,所以从它们被创建开始就具有相互独立的生命周期。
  • 指针也可以指向另一个指针,并且可以进行任意深度的嵌套,指针也可以指向另一个指针,并且可以进行任意深度的嵌套,
  • 对一个空指针的反向引用是不合法的,并且会使程序崩溃。

如果觉得我的文章对您有用,请在支付宝公益平台找个项目捐点钱。 @Victor Mar 4, 2019

奉献爱心