Go字符串常用函数


统计字符串长度

package main
import "fmt"
func main() {
    str := "study hard and make progress."
    fmt.Println(len(str))    //按字节算 len(string)
}//29

字符串遍历

package main
import "fmt"
func main() {
    str := "hi 北京欢迎你"
    for i, ch := range str {//range 可处理中文编码问题
        fmt.Printf("str[%d]=%c\n", i, ch)
    }  
}// output
str[0]=h
str[1]=i
str[2]=
str[3]=北
str[6]=京
str[9]=欢
str[12]=迎
str[15]=你
下标的不确定性 切片遍历方式 1 可以先将字符串转成 []rune 切片 2 用常规方法进行遍历
package main
import "fmt"
func main() {
    str := "hi 北京欢迎你"
    str2 := []rune(str)
    for i := 0; i < len(str2); i++ {
        fmt.Printf("str[%d]=%c\n", i, str2[i])
    }
}//output
str[0]=h
str[1]=i
str[2]=
str[3]=北
str[4]=京
str[5]=欢
str[6]=迎
str[7]=你
Go 中字符串类型 一串固定长度的字符连接起来的字符序列  Go字符串 由单个字节连接起来的
Go语言 字符串的字节使用UTF-8编码 标识Unicode文本  Golang统一使用UTF-8编码 中文乱码问题不再困扰程序员
字符串一旦赋值  不能修改 Go中字符串 不可变
常规遍历
str := "welcome 北京欢迎你"
for i := 0; i < len(str); i++ {
        fmt.Printf("str[%d]=%c\n", i, str[i])
} 是按照字节遍历 如果有中文等非英文字符 就会出现乱码

类型转换

字符串转数字 字符串转bool值等
web 参数是 string  需要转换成其他类型 官方strconv包 转换方法
两个函数 实现类型的互转 int转string 例
FormatInt (int64,base int)string
Itoa(int)string
strconv包 Itoa 实现方式 // Itoa is shorthand for FormatInt(int64(i), 10).
func Itoa(i int) string {
    return FormatInt(int64(i), 10)
} itoa 是便捷版的FormatInt  其他实现也类似

int 和string 互转
s := strconv.Itoa(i) //int 转化为string
s := strconv.FormatInt(int64(i), 10) //强制转化为int64后使用FormatInt
i, err := strconv.Atoi(s) //string 转为int
int64 和 string 互转
s := strconv.FormatInt(i64, 10) //int64 转 string 第二个参数为基数
//第二参数为基数 后面为位数 可以转换为int32 int64等
i64, err := strconv.ParseInt(s, 10, 64) // string 转换为 int64
float 和 string 互转
// flaot 转为string 最后一位是位数设置float32或float64
s1 := strconv.FormatFloat(v, 'E', -1, 32)
//string 转 float 同样最后一位设置位数
v, err := strconv.ParseFloat(s, 32)
v, err := strconv.atof32(s)
bool 和 string 互转
// ParseBool returns the boolean value represented by the string.
// It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False.
// Any other value returns an error.
func ParseBool(str string) (bool, error) {
    switch str {
    case "1", "t", "T", "true", "TRUE", "True":
        return true, nil
    case "0", "f", "F", "false", "FALSE", "False":
        return false, nil
    }
    return false, syntaxError("ParseBool", str)
}
// FormatBool returns "true" or "false" according to the value of b
func FormatBool(b bool) string {
    if b {
        return "true"
    }
    return "false"
}
//上面是官方实现 字符串t true 1都是真值
b, err := strconv.ParseBool("true") // string 转bool
s := strconv.FormatBool(true) // bool 转string

interface转其他类型
返回值是interface类型的 直接赋值无法转化
var a interface{}
var b string
a = "123"
b = a.(string)
通过a.(string) 转化为string 通过v.(int)转化为类型
通过a.(type) 判断a可以转为什么类型

字符串查找

strings.Contains() 知 strings.Contains() 判断的结果 返回bool值
是否存在某个字符或子串 三个函数
func Contains(s, substr string) bool    // 子串substr在s中 返回true
func ContainsAny(s, chars string) bool   //chars 任何一个Unicode代码点在s中 返回true
func ContainsRune(s string, r rune) bool // Unicode代码点r在s中 返回true
ContainsAny例子
fmt.Println(strings.ContainsAny("team", "i"))//false
fmt.Println(strings.ContainsAny("failure", "u & i"))//true
fmt.Println(strings.ContainsAny("in failure", "s g"))//true
fmt.Println(strings.ContainsAny("foo", ""))//false
fmt.Println(strings.ContainsAny("", ""))//false
第二个参数 chars 中任意一个字符(Unicode Code Point) 在第一个参数 s 中存在 返回true
三个函数 只是调用了相应的Index函数(子串出现的位置) 然后和 0 作比较返回true或fale
func Contains(s, substr string) bool {
    return Index(s, substr) >= 0
}

字符串统计

strings.Count() 子串出现次数(字符串匹配)
数据结构与算法中 讲解字符串匹配算法
朴素匹配算法
KMP算法
Rabin-Karp算法
Boyer-Moore算法
还有其他的算法
Go查找子串出现次数 即字符串模式匹配 实现的是Rabin-Karp算法
func Count(s, sep string) int
Count实现 处理了几种特殊情况 属于字符匹配预处理的一部分
当 sep 为空时 Count 的返回值是 utf8.RuneCountInString(s) + 1
fmt.Println(strings.Count("five", "")) // before & after each rune 输出 5
Count 是计算子串在字符串中出现的无重叠的次数
fmt.Println(strings.Count("fivevev", "vev")) 输出1

字符串比较

不区大小写 strings.EqualFold() 区分使用"=="比较
s, t := "hello go", "hello Go"
is_equal := strings.EqualFold(s, t)// 判断两个utf-8编码字符串 大小写不敏感
fmt.Println("EqualFold: ", is_equal) // EqualFold:  true

字符或子串在字符串中出现的位置

func Index(s, sep string) int  //s中查找 sep 第一次出现 返回第一次出现的索引
func IndexAny(s, chars string) int //chars任何一个Unicode代码点在s中首次出现的位置
func IndexFunc(s string, f func(rune) bool) int //查找字符 c 在 s 中第一次出现的位置 其中 c 满足 f(c) 返回 true
func IndexRune(s string, r rune) int //Unicode 代码点 r 在 s 中第一次出现的位置
//三个对应的查找最后一次出现的位置
func LastIndex(s, sep string) int
func LastIndexAny(s, chars string) int
func LastIndexFunc(s string, f func(rune) bool) int
fmt.Printf("%d\n", strings.IndexFunc("studygolang", func(c rune) bool {
    if c > 'u' {
        return true
    }
    return false
}))输出 4
因为 y 的 Unicode 代码点大于 u 的代码点

字符串重复几次

func Repeat(s string, count int) string
fmt.Println("ba" + strings.Repeat("na", 2))// 输出 banana

字符串替换

strings.Replace(s, old, new, n) n表示替换次数 -1代表替所有
进行字符串替换 考虑到性能 尽量别用正则 用这里的函数
// 用 new 替换 s 中的 old 一共替换 n 个
// 如果 n < 0,则不限制替换次数,即全部替换
func Replace(s, old, new string, n int) string
fmt.Println(strings.Replace("oink oink oink", "k", "ky", 2)) //oinky oinky oink
fmt.Println(strings.Replace("oink oink oink", "oink", "moo", -1))//moo moo moo
一次替换多个This is <b>HTML</b> 中的 < 和 > 为 &lt; 和 &gt; 调用上面的函数两次 标准库提供了另外的方法进行这种替换
Replacer 类型
一个结构没有导出字段 实例化通过 func NewReplacer(oldnew ...string) *Replacer 函数进行 不定参数 oldnew 是 old-new 对 即进行多个替换
r := strings.NewReplacer("<", "&lt;", ">", "&gt;")
fmt.Println(r.Replace("This is <b>HTML</b>!"))
Replacer 还提供了另外一个方法
func (r *Replacer) WriteString(w io.Writer, s string) (n int, err error)
它在替换之后将结果写入 io.Writer 中

字符串拆分

为数组strings.Split(s, sep) sep表示以什么什么来拆分s字符串
Split 和 SplitAfter
SplitN 和 SplitAfterN
这四个函数 都是通过同一个内部函数来实现的
函数签名及其实现
func Split(s, sep string) []string { return genSplit(s, sep, 0, -1) }
func SplitAfter(s, sep string) []string { return genSplit(s, sep, len(sep), -1) }
func SplitN(s, sep string, n int) []string { return genSplit(s, sep, 0, n) }
func SplitAfterN(s, sep string, n int) []string { return genSplit(s, sep, len(sep), n) }
都调用了 genSplit 函数
这四个函数都是通过 sep 进行分割 返回[]string
如果 sep 为空 相当于分成一个个的 UTF-8 字符
如 Split("abc","") 得到的是[a b c]
Split(s, sep) 和 SplitN(s, sep, -1) 等价
SplitAfter(s, sep) 和 SplitAfterN(s, sep, -1) 等价。
Split 和 SplitAfter 有啥区别
fmt.Printf("%q\n", strings.Split("foo,bar,baz", ","))
fmt.Printf("%q\n", strings.SplitAfter("foo,bar,baz", ","))
["foo" "bar" "baz"]
["foo," "bar," "baz"]
Split 会将 s 中的 sep 去掉 而 SplitAfter 会保留 sep
带 N 的方法可以通过最后一个参数 n 控制返回的结果中的 slice 中的元素个数
当 n < 0 时 返回所有的子字符串
当 n == 0 时 返回的结果是 nil
当 n > 0 时 表示返回的 slice 中最多只有 n 个元素
最后一个元素不会分割 如
fmt.Printf("%q\n", strings.SplitN("foo,bar,baz", ",", 2))
输出["foo" "bar,baz"]
fmt.Printf("%q\n", strings.Split("a,b,c", ",")) //["a" "b" "c"]
fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a "))//["" "man " "plan " "canal panama"]
fmt.Printf("%q\n", strings.Split(" xyz ", ""))//[" " "x" "y" "z" " "]
fmt.Printf("%q\n", strings.Split("", "Bernardo O'Higgins"))//[""]

字符串 JOIN 操作

通过 Join 将字符串数组(或slice)连接起来
func Join(a []string, sep string) string
func Join(str []string, sep string) string {  //假如没有这个 Join 库函数 自己实现
    if len(str) == 0 {// 特殊情况应该做处理
        return ""
    }
    if len(str) == 1 {
        return str[0]
    }
    buffer := bytes.NewBufferString(str[0])
    for _, s := range str[1:] {
        buffer.WriteString(sep)
        buffer.WriteString(s)
    }
    return buffer.String()
}
使用了 bytes 包的 Buffer 类型,避免大量的字符串连接操作 Go 中字符串是不可变的
标准库的实现
func Join(a []string, sep string) string {
    if len(a) == 0 {
        return ""
    }
    if len(a) == 1 {
        return a[0]
    }
    n := len(sep) * (len(a) - 1)
    for i := 0; i < len(a); i++ {
        n += len(a[i])
    }

    b := make([]byte, n)
    bp := copy(b, a[0])
    for _, s := range a[1:] {
        bp += copy(b[bp:], sep)
        bp += copy(b[bp:], s)
    }
    return string(b)
}
没有用 bytes 包 Go 不允许循环依赖
标准库中 出现代码拷贝而不是引入某个包
fmt.Println(Join([]string{"name=xxx", "age=xx"}, "&"))// 输出 name=xxx&age=xx

字符串大小写转换

strings.ToUpper(s)转大写 strings.ToLower(s)转小写
func ToUpper(s string) string // 返回将所有字母都转为对应的大写版本的拷贝
去除左或右空格等特殊字符 去除左右两边空格
去除左边两边指定字符去除strings.Trim(s, cutset) cutset代表要去除的两边字符举例略
将左边指定支符去掉strings.TrimLeft(s, cutset)
将右边指定支符去掉strings.TrimRight(s, cutset)

 判断字符串开头或结尾

开头strings.HasPrefix(s, prefix)
结尾strings.HasSuffix(s, suffix)
func HasPrefix(s, prefix string) bool {// s 中是否以 prefix 开始
    return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}
func HasSuffix(s, suffix string) bool {// s 中是否以 suffix 结尾
    return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
}

Reader 类型

实现了 io 包中的接口
io.Reader(Read 方法)
io.ReaderAt(ReadAt 方法)
io.Seeker(Seek 方法)
io.WriterTo(WriteTo 方法)
io.ByteReader(ReadByte 方法)
io.ByteScanner(ReadByte 和 UnreadByte 方法)
io.RuneReader(ReadRune 方法)
io.RuneScanner(ReadRune 和 UnreadRune 方法)
Reader 结构
type Reader struct {
    s        string // Reader 读取的数据来源
    i        int // current reading index(当前读的索引位置)
    prevRune int // index of previous rune; or < 0(前一个读取的 rune 索引位置)
}
Reader 结构没有导出任何字段 而是提供一个实例化方法
func NewReader(s string) *Reader
该方法接收一个字符串
返回的 Reader 实例就是从该参数字符串读数据
bytes.NewBufferString 有类似的功能
如果只是为了读取 NewReader 会更高效