# 文本

# strings

字符串常见操作有:

  • 字符串长度;

  • 求子串;

  • 是否存在某个字符或子串;

  • 子串出现的次数(字符串匹配);

  • 字符串分割(切分)为 [] string;

  • 字符串是否有某个前缀或后缀;

  • 字符或子串在字符串中首次出现的位置或最后一次出现的位置;

  • 通过某个字符串将 [] string 连接起来;

  • 字符串重复几次;

  • 字符串中子串替换;

  • 大小写转换;

  • Trim 操作;

  • ...

    string 类型可以看成是一种特殊的 slice 类型,因此获取长度可以用内置的函数 len;同时支持 切片 操作,因此,子串获取很容易。

    这里说的字符,指得是 rune 类型,即一个 UTF-8 字符(Unicode 代码点)。

# Compare, EqualFold

字符串比较

// go 1.20 strings/compare.go 
func Compare(a, b string) int {
    // 注意 (rsc):该函数不会调用运行时 cmpstring 函数,因为我们不想为使用 strings.Compare 提供任何性能理由。 基本上没有人应该使用 strings.Compare。
	if a == b {
		return 0
	}
	if a < b {
		return -1
	}
	return +1
}
// 忽略大小写
func EqualFold(s, t string) bool

demo :

func main() {
	a := "gopher"
	b := "hello world"
	fmt.Println(strings.Compare(a, b)) // -1
	fmt.Println(strings.Compare(a, a)) // 0
	fmt.Println(strings.Compare(b, a)) // 1
	fmt.Println(strings.EqualFold("GO", "go")) // true
	fmt.Println(strings.EqualFold("壹", "一"))   // false
}
Contains ContainsAny ContainsRune

# Contains

是否存在某个字符或子串

Contains, ContainsAny, ContainsRune

// Contains 子串 substr 在 s 中,返回 true
func Contains(s, substr string) bool {
	return Index(s, substr) >= 0
}
//chars 中任何一个 Unicode 码位在 s 中,返回 true
func ContainsAny(s, chars string) bool {
	return IndexAny(s, chars) >= 0
}
// Unicode 码位 r 在 s 中,返回 true
func ContainsRune(s string, r rune) bool {
	return IndexRune(s, r) >= 0
}

demo:

func main() {
	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
}

# Count

在数据结构与算法中,可能会讲解以下字符串匹配算法:

  • 朴素匹配算法
  • KMP 算法
  • Rabin-Karp 算法
  • Boyer-Moore 算法

在 Go 中,查找子串出现次数即字符串模式匹配,实现的是 Rabin-Karp 算法。:

func Count(s, substr string) int {
	// special case
	if len(substr) == 0 {
		return utf8.RuneCountInString(s) + 1
	}
	if len(substr) == 1 {
		return bytealg.CountString(s, substr[0])
	}
	n := 0
	for {
		i := Index(s, substr)
		if i == -1 {
			return n
		}
		n++
		s = s[i+len(substr):]
	}
}
// IndexRabinKarp uses the Rabin-Karp search algorithm to return the index of the
// first occurrence of substr in s, or -1 if not present.
func IndexRabinKarp(s, substr string) int {
	// Rabin-Karp search
	hashss, pow := HashStr(substr)
	n := len(substr)
	var h uint32
	for i := 0; i < n; i++ {
		h = h*PrimeRK + uint32(s[i])
	}
	if h == hashss && s[:n] == substr {
		return 0
	}
	for i := n; i < len(s); {
		h *= PrimeRK
		h += uint32(s[i])
		h -= pow * uint32(s[i-n])
		i++
		if h == hashss && s[i-n:i] == substr {
			return i - n
		}
	}
	return -1
}

当 sep 为空时,Count 的返回值是:utf8.RuneCountInString (s) + 1

demo:

func main() {
	fmt.Println(strings.Count("cheese", "e"))    // 3
	fmt.Println(len("谷歌中国"))                  // 12
	fmt.Println(strings.Count("谷歌中国", ""))    // 5
}

# Split

字符串分割为 [] string

该包提供了六个三组分割函数:Fields 和 FieldsFunc、Split 和 SplitAfter、SplitN 和 SplitAfterN。

Fields , FieldsFunc

func Fields(s string) []string
func FieldsFunc(s string, f func(rune) bool) []string

Fields 用一个或多个连续的空格分隔字符串 s,返回子字符串的数组(slice)。如果字符串 s 只包含空格,则返回空列表 ([] string 的长度为 0)。其中,空格的定义是 unicode.IsSpace,之前已经介绍过。

常见间隔符包括: '\t', '\n', '\v', '\f', '\r', ' ', U+0085 (NEL), U+00A0 (NBSP)

由于是用空格分隔,因此结果中不会含有空格或空子字符串,例如:

fmt.Printf("Fields are: %q", strings.Fields("  foo bar  baz   "))
// Fields are: ["foo" "bar" "baz"]

FieldsFunc 用 Unicode 码位进行分隔:满足 f (c) 返回 true。该函数返回 [] string。如果字符串 s 中所有的码位 (unicode code points) 都满足 f (c) 或者 s 是空,则 FieldsFunc 返回空 slice。

也就是说,我们可以通过实现一个回调函数来指定分隔字符串 s 的字符。比如上面的例子,我们通过 FieldsFunc 来实现:

fmt.Println(strings.FieldsFunc("  foo bar  baz   ", unicode.IsSpace))

Split 和 SplitAfter、 SplitN 和 SplitAfterN

都是调用 genSplit 函数

// 分割字符串
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) }
// 分割字符串,返回长度为 n 的切片
func SplitN(s, sep string, n int) []string { return genSplit(s, sep, 0, n) }
// 在切割符号后,分割字符串,返回长度为 n 的切片
func SplitAfterN(s, sep string, n int) []string { return genSplit(s, sep, len(sep), n) }
// Generic split: splits after each instance of sep,
// including sepSave bytes of sep in the subarrays.
func genSplit(s, sep string, sepSave, n int) []string {
	if n == 0 {
		return nil
	}
	if sep == "" {
		return explode(s, n)
	}
	if n < 0 {
		n = Count(s, sep) + 1
	}
	if n > len(s)+1 {
		n = len(s) + 1
	}
	a := make([]string, n)
	n--
	i := 0
	for i < n {
		m := Index(s, sep)
		if m < 0 {
			break
		}
		a[i] = s[:m+sepSave]
		s = s[m+len(sep):]
		i++
	}
	a[i] = s
	return a[:i+1]
}

demo:

func main() {
	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"))            // [""]
	fmt.Printf("%q\n", strings.SplitN("a,b,c", ",", 2))      // ["a" "b,c"]
	fmt.Printf("%q\n", strings.SplitAfter("a,b,c", ","))     // ["a," "b," "c"]
	fmt.Printf("%q\n", strings.SplitAfterN("a,b,c", ",", 2)) // ["a," "b,c"]
}

# HasPrefix, HasSuffix

字符串是否有某个前缀或后缀

// HasPrefix tests whether the string s begins with prefix.
func HasPrefix(s, prefix string) bool {
	return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}
// HasSuffix tests whether the string s ends with suffix.
func HasSuffix(s, suffix string) bool {
	return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
}

demo :

func main() {
	fmt.Println(strings.HasPrefix("Gopher", "Go")) // true
	fmt.Println(strings.HasPrefix("Gopher", "C"))  // false
	fmt.Println(strings.HasPrefix("Gopher", ""))   // true
	fmt.Println(strings.HasSuffix("Amigo", "go"))  // true
	fmt.Println(strings.HasSuffix("Amigo", "Ami")) // false
	fmt.Println(strings.HasSuffix("Amigo", ""))    // true
}

# Index

// 在 s 中查找 sep 的第一次出现,返回第一次出现的索引
func Index(s, sep string) int
// 在 s 中查找字节 c 的第一次出现,返回第一次出现的索引
func IndexByte(s string, c byte) int
//chars 中任何一个 Unicode 码位 在 s 中首次出现的位置
func IndexAny(s, chars string) int
// 查找字符 c 在 s 中第一次出现的位置,其中 c 满足 f (c) 返回 true
func IndexFunc(s string, f func(rune) bool) int
// Unicode 码位 r 在 s 中第一次出现的位置
func IndexRune(s string, r rune) int
// 有三个对应的查找最后一次出现的位置
func LastIndex(s, sep string) int
func LastIndexByte(s string, c byte) int
func LastIndexAny(s, chars string) int
func LastIndexFunc(s string, f func(rune) bool) int

demo:

func main() {
	han := func(c rune) bool {
		return unicode.Is(unicode.Han, c) // 汉字
	}
	fmt.Println(strings.Index("Hello, world", ","))     // 5
	fmt.Println(strings.LastIndex("Hello, world", "l")) // 10
	fmt.Println(strings.IndexFunc("Hello, world", han)) // -1
	fmt.Println(strings.IndexFunc("Hello, 世界", han))    // 7
}

# Join

// Join concatenates the elements of its first argument to create a single string. The separator
// string sep is placed between elements in the resulting string.
func Join(elems []string, sep string) string {
	switch len(elems) {
	case 0:
		return ""
	case 1:
		return elems[0]
	}
	n := len(sep) * (len(elems) - 1)
	for i := 0; i < len(elems); i++ {
		n += len(elems[i])
	}
    // Builder 避免了大量字符串连接操作,最大限度地减少了内存复制
	var b Builder
	b.Grow(n)
	b.WriteString(elems[0])
	for _, s := range elems[1:] {
		b.WriteString(sep)
		b.WriteString(s)
	}
	return b.String()
}

demo:

func main() {
	fmt.Println(strings.Join([]string{"name=xxx", "age=xx"}, "&"))
    // name=xxx&age=xx
}

# Replace

// ReplaceAll 返回字符串 s 的副本,其中包含所有内容
func ReplaceAll(s, old, new string) string {
	return Replace(s, old, new, -1)
}
// Replace 返回字符串 s 的前 n 个副本
func Replace(s, old, new string, n int) string {
	if old == new || n == 0 {
		return s // 避免分配内存
	}
	// 计算 Replace 的次数
	if m := Count(s, old); m == 0 {
		return s // avoid allocation
	} else if n < 0 || m < n {
		n = m
	}
	// Apply replacements to buffer.
	var b Builder
	b.Grow(len(s) + n*(len(new)-len(old)))
	start := 0
	for i := 0; i < n; i++ {
		j := start
		if len(old) == 0 {
			if i > 0 {
				_, wid := utf8.DecodeRuneInString(s[start:])
				j += wid
			}
		} else {
			j += Index(s[start:], old)
		}
		b.WriteString(s[start:j])
		b.WriteString(new)
		start = j + len(old)
	}
	b.WriteString(s[start:])
	return b.String()
}

demo :

func main() {
	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
	fmt.Println(strings.ReplaceAll("oink oink oink", "oink", "moo"))  // moo moo moo
}

# Trim

// 将 s 左侧和右侧中匹配 cutset 中的任一字符的字符去掉
func Trim(s string, cutset string) string
// 将 s 左侧的匹配 cutset 中的任一字符的字符去掉
func TrimLeft(s string, cutset string) string
// 将 s 右侧的匹配 cutset 中的任一字符的字符去掉
func TrimRight(s string, cutset string) string
// 如果 s 的前缀为 prefix 则返回去掉前缀后的 string , 否则 s 没有变化。
func TrimPrefix(s, prefix string) string
// 如果 s 的后缀为 suffix 则返回去掉后缀后的 string , 否则 s 没有变化。
func TrimSuffix(s, suffix string) string
// 将 s 左侧和右侧的间隔符去掉。常见间隔符包括:'\t', '\n', '\v', '\f', '\r', ' ', U+0085 (NEL)
func TrimSpace(s string) string
// 将 s 左侧和右侧的匹配 f 的字符去掉
func TrimFunc(s string, f func(rune) bool) string
// 将 s 左侧的匹配 f 的字符去掉
func TrimLeftFunc(s string, f func(rune) bool) string
// 将 s 右侧的匹配 f 的字符去掉
func TrimRightFunc(s string, f func(rune) bool) string

demo :

func main() {
	x := "!!!@@@你好,!@#$ Gophers###$$$"
	fmt.Println(strings.Trim(x, "@#$!%^&*()_+=-"))                  // 你好,!@#$ Gophers
	fmt.Println(strings.TrimLeft(x, "@#$!%^&*()_+=-"))              // 你好,!@#$ Gophers###$$$
	fmt.Println(strings.TrimRight(x, "@#$!%^&*()_+=-"))             // !!!@@@你好,!@#$ Gophers
	fmt.Println(strings.TrimSpace(" \t\n Hello, Gophers \n\t\r\n")) // Hello, Gophers
	fmt.Println(strings.TrimPrefix(x, "!"))                         // !!@@@你好,!@#$ Gophers###$$$
	fmt.Println(strings.TrimSuffix(x, "$"))                         // !!!@@@你好,!@#$ Gophers###$$
	f := func(r rune) bool {
		return !unicode.Is(unicode.Han, r) // 非汉字返回 true
	}
	fmt.Println(strings.TrimFunc(x, f))      // 你好
	fmt.Println(strings.TrimLeftFunc(x, f))  // 你好,!@#$ Gophers###$$$
	fmt.Println(strings.TrimRightFunc(x, f)) // !!!@@@你好
}

# Case

大小写转换

如果不是 ASCII 码,调用的都是 unicode 的方法

func ToLower(s string) string
func ToLowerSpecial(c unicode.SpecialCase, s string) string
func ToUpper(s string) string
func ToUpperSpecial(c unicode.SpecialCase, s string) string

demo :

func main() {
	fmt.Println(strings.ToLower("HELLO WORLD"))                             // hello world
	fmt.Println(strings.ToLower("Ā Á Ǎ À"))                                 // ā á ǎ à
	fmt.Println(strings.ToLowerSpecial(unicode.TurkishCase, "壹"))           // 壹
	fmt.Println(strings.ToLowerSpecial(unicode.TurkishCase, "HELLO WORLD")) // hello world
	fmt.Println(strings.ToLower("Önnek İş"))                                // önnek iş
	fmt.Println(strings.ToLowerSpecial(unicode.TurkishCase, "Önnek İş"))    // önnek iş
}

# bytes

该包定义了一些操作 byte slice 的便利操作。因为字符串可以表示为 []byte ,因此,bytes 包定义的函数、方法等和 strings 包很类似.

说明:为了方便,会称呼 []byte 为 字节数组

# Contains

是否存在某个子 slice ,和 string 类似

// Contains 子串 substr 在 s 中,返回 true
func Contains(b, subslice []byte) bool {
	return Index(b, subslice) != -1
}
//chars 中任何一个 Unicode 码位在 s 中,返回 true
func ContainsAny(b []byte, chars string) bool {
	return IndexAny(b, chars) >= 0
}
// Unicode 码位 r 在 s 中,返回 true
func ContainsRune(b []byte, r rune) bool {
	return IndexRune(b, r) >= 0
}

demo:

func main() {
	a := []byte{'A', 'S', 'D', 'Q', 'W', 'e'}
	fmt.Println(bytes.Contains(a, []byte{'e'}))   // true
	fmt.Println(bytes.Contains(a, []byte{'E'}))   // false
	fmt.Println(bytes.Contains(a, []byte{'f'}))   // false
	fmt.Println(bytes.ContainsAny(a, "ASD"))      // true
	fmt.Println(bytes.ContainsRune(a, rune('A'))) // true
}

# Count

记录 []byte 出现的次数

func Count(s, sep []byte) int {
	// special case
	if len(sep) == 0 {
		return utf8.RuneCount(s) + 1
	}
	if len(sep) == 1 {
		return bytealg.Count(s, sep[0])
	}
	n := 0
	for {
		i := Index(s, sep)
		if i == -1 {
			return n
		}
		n++
		s = s[i+len(sep):]
	}
}

demo:

func main() {
	a := []byte{'A', 'S', 'D', 'Q', 'W', 'e', 'e'}
	fmt.Println(bytes.Count(a, []byte{'e'}))      // 2
	fmt.Println(bytes.Count(a, []byte{'e', 'e'})) // 1
	fmt.Println(bytes.Count(a, []byte{'e', 'W'})) // 0
}

# Runes

Runes 类型转换

// 将 [] byte 转换为 [] rune
func Runes(s []byte) []rune {
	t := make([]rune, utf8.RuneCount(s))
	i := 0
	for len(s) > 0 {
		r, l := utf8.DecodeRune(s)
		t[i] = r
		i++
		s = s[l:]
	}
	return t
}

demo :

func main() {
	b := []byte("你好,世界")
	for k, v := range b {
		fmt.Printf("%d:%s |", k, string(v))
	}
	r := bytes.Runes(b)
	for k, v := range r {
		fmt.Printf("%d:%s|", k, string(v))
	}
}
// 0:ä |1:½ |2:  |3:å |4:¥ |5:½ |6:ï |7:¼ |8: |9:ä |10:¸ |11: |12:ç |13: |14: |
// 0: 你 |1: 好 |2:, |3: 世 |4: 界 |

# Reader

type Reader struct {
	s        []byte
	i        int64 // 当前下标
	prevRune int   // 前一个字符的下标,存在 < 0 的情况
}

bytes 包下的 Reader 类型实现了 io 包下的 Reader, ReaderAt, RuneReader, RuneScanner, ByteReader, ByteScanner, ReadSeeker, Seeker, WriterTo 等多个接口。主要用于 Read 数据。

与 Buffer 不同,Reader 是只读的并且支持查找。

Reader 的零值操作就像空切片的 Reader 一样。

Reader 包含了 8 个读取相关的方法,实现了前面提到的 io 包下的 9 个接口(ReadSeeker 接口内嵌 Reader 和 Seeker 两个接口):

// 读取数据至 b 
func (r *Reader) Read(b []byte) (n int, err error) 
// 读取一个字节
func (r *Reader) ReadByte() (byte, error)
// 读取一个字符
func (r *Reader) ReadRune() (ch rune, size int, err error)
// 读取数据至 w
func (r *Reader) WriteTo(w io.Writer) (n int64, err error)
// 进度下标指向前一个字节,如果 r.i <= 0 返回错误。
func (r *Reader) UnreadByte() 
// 进度下标指向前一个字符,如果 r.i <= 0 返回错误,且只能在每次 ReadRune 方法后使用一次,否则返回错误。
func (r *Reader) UnreadRune() 
// 读取 r.s [off:] 的数据至 b,该方法忽略进度下标 i,不使用也不修改。
func (r *Reader) ReadAt(b []byte, off int64) (n int, err error) 
// 根据 whence 的值,修改并返回进度下标 i ,当 whence == 0 ,进度下标修改为 off,当 whence == 1 ,进度下标修改为 i+off,当 whence == 2 ,进度下标修改为 len [s]+off.
//off 可以为负数,whence 的只能为 0,1,2,当 whence 为其他值或计算后的进度下标越界,则返回错误。
func (r *Reader) Seek(offset int64, whence int) (int64, error)

demo :

func main() {
	x := []byte("你好,世界")
	r1 := bytes.NewReader(x)
	ch, size, _ := r1.ReadRune()
	fmt.Println(size, string(ch)) // 3 你
	_ = r1.UnreadRune()
	ch, size, _ = r1.ReadRune()
	fmt.Println(size, string(ch)) // 3 你
	_ = r1.UnreadRune()
	by, _ := r1.ReadByte()
	fmt.Println(by) // 228
	_ = r1.UnreadByte()
	by, _ = r1.ReadByte()
	fmt.Println(by) // 228
	_ = r1.UnreadByte()
	d1 := make([]byte, 6)
	n, _ := r1.Read(d1)
	fmt.Println(n, string(d1)) // 6 你好
	d2 := make([]byte, 6)
	n, _ = r1.ReadAt(d2, 0)
	fmt.Println(n, string(d2)) // 6 你好
	w1 := &bytes.Buffer{}
	_, _ = r1.Seek(0, 0)
	_, _ = r1.WriteTo(w1)
	fmt.Println(w1.String()) // 你好,世界
}

# Buffer

Buffer 是一个可变大小的字节缓冲区,具有 Read 和 Write 方法。

Buffer 的零值是一个可以使用的空缓冲区。

type Buffer struct {
	buf      []byte // contents are the bytes buf[off : len(buf)]
	off      int    // read at &buf[off], write at &buf[len(buf)]
	lastRead readOp // last read operation, so that Unread* can work correctly.
}

Buffer 包含了 21 个读写相关的方法,大部分同名方法的用法与前面讲的类似,这里只讲演示其中的 3 个方法:

// 读取到字节 delim 后,以字节数组的形式返回该字节及前面读取到的字节。如果遍历 b.buf 也找不到匹配的字节,则返回错误 (一般是 EOF)
func (b *Buffer) ReadBytes(delim byte) (line []byte, err error)
// 读取到字节 delim 后,以字符串的形式返回该字节及前面读取到的字节。如果遍历 b.buf 也找不到匹配的字节,则返回错误 (一般是 EOF)
func (b *Buffer) ReadString(delim byte) (line string, err error)
// 截断 b.buf , 舍弃 b.off+n 之后的数据。n == 0 时,调用 Reset 方法重置该对象,当 n 越界时(n < 0 || n > b.Len () )方法会触发 panic.
func (b *Buffer) Truncate(n int)

demo :

func main() {
	a := bytes.NewBufferString("Good Night")
	x, err := a.ReadBytes('t')
	if err != nil {
		fmt.Println("delim:t err:", err)
	} else {
		fmt.Println(string(x)) // Good Night
	}
	a.Truncate(0)
	a.WriteString("Good Night")
	fmt.Println(a.Len()) // 10
	a.Truncate(5)
	fmt.Println(a.Len()) // 5
	y, err := a.ReadString('N')
	if err != nil {
		fmt.Println("delim:N err:", err) // delim:N err: EOF
	} else {
		fmt.Println(y)
	}
}

# strconv

字符串和基本数据类型之间转换

这里的基本数据类型包括:布尔、整型(包括有 / 无符号、二进制、八进制、十进制和十六进制)和浮点型等。

# Error

strconv 中的错误处理。

由于将字符串转为其他数据类型可能会出错,strconv 包定义了两个 error 类型的变量:ErrRange 和 ErrSyntax。

ErrRange 表示值超过了类型能表示的最大范围,比如将 "128" 转为 int8 就会返回这个错误;
ErrSyntax 表示语法错误,比如将 "" 转为 int 类型会返回这个错误。

然而,在返回错误的时候,不是直接将上面的变量值返回,而是通过构造一个 NumError 类型的 error 对象返回。NumError 结构的定义如下:

// NumError 记录失败的转换。
type NumError struct {
	Func string // 失败的函数 (ParseBool, ParseInt, ParseUint, ParseFloat, ParseComplex)
	Num  string // 输入
	Err  error  // 转换失败的原因 (e.g. ErrRange, ErrSyntax, etc.)
}

实现了 error 接口

func (e *NumError) Error() string {
	return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
}

构造 error

func syntaxError(fn, str string) *NumError {
    return &NumError{fn, str, ErrSyntax}
}
func rangeError(fn, str string) *NumError {
    return &NumError{fn, str, ErrRange}
}

# int

字符串和整型之间的转换

字符串转为整型

func ParseInt(s string, base int, bitSize int) (i int64, err error)
func ParseUint(s string, base int, bitSize int) (n uint64, err error)
func Atoi(s string) (i int, err error)

Atoi 是 ParseInt 的便捷版,内部通过调用 ParseInt(s, 10, 0) 来实现的;
ParseInt 转为有符号整型;
ParseUint 转为无符号整型;

参数 base 代表字符串按照给定的进制进行解释。一般的,base 的取值为 2~36,如果 base 的值为 0,则会根据字符串的前缀来确定 base 的值:"0x" 表示 16 进制; "0" 表示 8 进制;否则就是 10 进制。

参数 bitSize 表示的是整数取值范围,或者说整数的具体类型。取值 0、8、16、32 和 64 分别代表 int、int8、int16、int32 和 int64。

当 bitSize==0 时

Go 中,int/uint 类型,不同系统能表示的范围是不一样的,目前的实现是,32 位系统占 4 个字节;64 位系统占 8 个字节。当 bitSize==0 时,应该表示 32 位还是 64 位呢?这里没有利用 runtime.GOARCH 之类的方式,而是巧妙的通过如下表达式确定 intSize:

const intSize = 32 << uint(^uint(0)>>63)
const IntSize = intSize // number of bits in int, uint (32 or 64)

整型转为字符串

func FormatUint(i uint64, base int) string    // 无符号整型转字符串
func FormatInt(i int64, base int) string    // 有符号整型转字符串
func Itoa(i int) string

Itoa 内部直接调用 FormatInt (i, 10)* 实现的。base 参数可以取 2~36(0-9,a-z)。

# bool

字符串和布尔值之间的转换比较简单,主要有三个函数:

// 接受 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False 等字符串;
// 其他形式的字符串会返回错误
func ParseBool(str string) (value bool, err error)
// 直接返回 "true" 或 "false"
func FormatBool(b bool) string
// 将 "true" 或 "false" append 到 dst 中
// 这里用了一个 append 函数对于字符串的特殊形式:append (dst, "true"...)
func AppendBool(dst []byte, b bool)

# float

字符串和浮点数之间的转换,包含三个函数:

func ParseFloat(s string, bitSize int) (f float64, err error)
func FormatFloat(f float64, fmt byte, prec, bitSize int) string
func AppendFloat(dst []byte, f float64, fmt byte, prec int, bitSize int)

由于浮点数有精度的问题,精度不一样,ParseFloat 和 FormatFloat 可能达不到互逆的效果。如:

s := strconv.FormatFloat(1234.5678, 'g', 6, 64)
strconv.ParseFloat(s, 64)

# unicode

go 对 unicode 包的支持,由于 UTF-8 的作者 Ken Thompson 同时也是 go 语言的创始人,所以说,在字符支持方面,几乎没有语言的理解会高于 go 了。 go 对 unicode 的支持包含三个包 :

  • unicode
  • unicode/utf8
  • unicode/utf16

unicode 包包含基本的字符判断函数。utf8 包主要负责 rune 和 byte 之间的转换。utf16 包负责 rune 和 uint16 数组之间的转换。

go 语言的所有代码都是 UTF8 的,所以如果我们在程序中的字符串都是 utf8 编码的,但是我们的单个字符(单引号扩起来的)却是 unicode 的。

# unicode

unicode 包含了对 rune 的判断。这个包把所有 unicode 涉及到的编码进行了分类,使用结构

type RangeTable struct {
    R16         []Range16
    R32         []Range32
    LatinOffset int
}

#

func IsControl(r rune) bool  // 是否控制字符
func IsDigit(r rune) bool  // 是否阿拉伯数字字符,即 0-9
func IsGraphic(r rune) bool // 是否图形字符
func IsLetter(r rune) bool // 是否字母
func IsLower(r rune) bool // 是否小写字符
func IsMark(r rune) bool // 是否符号字符
func IsNumber(r rune) bool // 是否数字字符,比如罗马数字 Ⅷ 也是数字字符
func IsOneOf(ranges []*RangeTable, r rune) bool // 是否是 RangeTable 中的一个
func IsPrint(r rune) bool // 是否可打印字符
func IsPunct(r rune) bool // 是否标点符号
func IsSpace(r rune) bool // 是否空格
func IsSymbol(r rune) bool // 是否符号字符
func IsTitle(r rune) bool // 是否 title case
func IsUpper(r rune) bool // 是否大写字符
func Is(rangeTab *RangeTable, r rune) bool //r 是否为 rangeTab 类型的字符
func In(r rune, ranges ...*RangeTable) bool  //r 是否为 ranges 中任意一个类型的字符

demo:

func main() {
	single := '\u0015'
	fmt.Println(unicode.IsControl(single)) // true
	single = '\ufe35'
	fmt.Println(unicode.IsControl(single)) // false
	digit := '1'
	fmt.Println(unicode.IsDigit(digit))  // true
	fmt.Println(unicode.IsNumber(digit)) // true
	letter := 'Ⅷ'
	fmt.Println(unicode.IsDigit(letter))  // false
	fmt.Println(unicode.IsNumber(letter)) // true
	han := '你'
	fmt.Println(unicode.IsDigit(han))                                   // false
	fmt.Println(unicode.Is(unicode.Han, han))                           // true
	fmt.Println(unicode.In(han, unicode.Gujarati, unicode.White_Space)) // false
}

# utf8

utf8 里面的函数就有一些字节和字符的转换。

// 判断是否符合 utf8 编码的函数:
func Valid(p []byte) bool
func ValidRune(r rune) bool
func ValidString(s string) bool
// 判断 rune 所占字节数:
func RuneLen(r rune) int
// 判断字节串或者字符串的 rune 数:
func RuneCount(p []byte) int
func RuneCountInString(s string) (n int)
// 编码和解码到 rune:
func EncodeRune(p []byte, r rune) int
func DecodeRune(p []byte) (r rune, size int)
func DecodeRuneInString(s string) (r rune, size int)
func DecodeLastRune(p []byte) (r rune, size int)
func DecodeLastRuneInString(s string) (r rune, size int)
// 是否为完整 rune:
func FullRune(p []byte) bool
func FullRuneInString(s string) bool
// 是否为 rune 第一个字节:
func RuneStart(b byte) bool

demo :

func main() {
	word := []byte("界")
	fmt.Println(utf8.Valid(word[:2]))   // false
	fmt.Println(utf8.ValidRune('界'))    // true
	fmt.Println(utf8.ValidString("世界")) // true
	fmt.Println(utf8.RuneLen('界')) // 3
	fmt.Println(utf8.RuneCount(word))         // 1
	fmt.Println(utf8.RuneCountInString("世界")) // 2
	p := make([]byte, 3)
	utf8.EncodeRune(p, '好')
	fmt.Println(p)                                 // [229 165 189]
	fmt.Println(utf8.DecodeRune(p))                // 22909 3
	fmt.Println(utf8.DecodeRuneInString("你好"))     // 20320 3
	fmt.Println(utf8.DecodeLastRune([]byte("你好"))) // 22909 3
	fmt.Println(utf8.DecodeLastRuneInString("你好")) // 22909 3
	fmt.Println(utf8.FullRune(word[:2]))     // false
	fmt.Println(utf8.FullRuneInString("你好")) // true
	fmt.Println(utf8.RuneStart(word[1])) // false
	fmt.Println(utf8.RuneStart(word[0])) // true
}

# uft16

// 将 uint16 和 rune 进行转换
func Encode(s []rune) []uint16
func EncodeRune(r rune) (r1, r2 rune)
func Decode(s []uint16) []rune
func DecodeRune(r1, r2 rune) rune
// 是否为有效代理对
func IsSurrogate(r rune) bool

unicode 有个基本字符平面和增补平面的概念,基本字符平面只有 65535 个字符,增补平面(有 16 个)加上去就能表示 1114112 个字符。 utf16 就是严格实现了 unicode 的这种编码规范。

而基本字符和增补平面字符就是一个代理对(Surrogate Pair)。一个代理对可以和一个 rune 进行转换。

demo:

func main() {
	words := []rune{'𝓐', '𝓑'}
	u16 := utf16.Encode(words)
	fmt.Println(u16)               // [55349 56528 55349 56529]
	fmt.Println(utf16.Decode(u16)) // [120016 120017]
	r1, r2 := utf16.EncodeRune('𝓐')
	fmt.Println(r1, r2)                   // 55349 56528
	fmt.Println(utf16.DecodeRune(r1, r2)) // 120016
	fmt.Println(utf16.IsSurrogate(r1))    // true
	fmt.Println(utf16.IsSurrogate(r2))    // true
	fmt.Println(utf16.IsSurrogate(1234))  // false
}
更新于
-->