2.2 bytes — byte slice 便利操作

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

    2.2.1 是否存在某个子 slice

    该函数的内部调用了 bytes.Index 函数(在后面会讲解):

    1. return Index(b, subslice) != -1
    2. }

    题外:对比 strings.Contains 你会发现,一个判断 >=0,一个判断 != -1,可见库不是一个人写的,没有做到一致性。

    1. // slice sep 在 s 中出现的次数(无重叠)
    2. func Count(s, sep []byte) int

    和 strings 实现不同,此包中的 Count 核心代码如下:

    1. count := 0
    2. c := sep[0]
    3. i := 0
    4. t := s[:len(s)-n+1]
    5. for i < len(t) {
    6. // 判断 sep 第一个字节是否在 t[i:] 中
    7. // 如果在,则比较之后相应的字节
    8. if t[i] != c {
    9. o := IndexByte(t[i:], c)
    10. if o < 0 {
    11. break
    12. }
    13. i += o
    14. }
    15. // 执行到这里表示 sep[0] == t[i]
    16. if n == 1 || Equal(s[i:i+n], sep) {
    17. count++
    18. i += n
    19. continue
    20. }
    21. i++
    22. }

    2.2.3 Runes 类型转换

    1. // 将 []byte 转换为 []rune
    2. func Runes(s []byte) []rune

    该函数将 []byte 转换为 []rune ,适用于汉字等多字节字符,示例:

    1. for k,v:=range b{
    2. fmt.Printf("%d:%s |",k,string(v))
    3. }
    4. for k,v:=range r{
    5. fmt.Printf("%d:%s|",k,string(v))
    6. }
    1. type Reader struct {
    2. s []byte
    3. i int64 // 当前读取下标
    4. prevRune int // 前一个字符的下标,也可能 < 0
    5. }

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

    我们需要在通过 bytes.NewReader 方法来初始化 bytes.Reader 类型的对象。初始化时传入 []byte 类型的数据。NewReader 函数签名如下:

    1. func NewReader(b []byte) *Reader

    如果直接声明该对象了,可以通过 Reset 方法重新写入数据,示例:

    1. x:=[]byte("你好,世界")
    2. r1:=bytes.NewReader(x)
    3. d1:=make([]byte,len(x))
    4. n,_:=r1.Read(d1)
    5. fmt.Println(n,string(d1))
    6. r2:=bytes.Reader{}
    7. r2.Reset(x)
    8. d2:=make([]byte,len(x))
    9. n,_=r2.Read(d2)
    10. fmt.Println(n,string(d2))

    输出结果:

    1. 15 你好,世界
    2. 15 你好,世界

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

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

    示例:

    1. 3
    2. 3
    3. 228
    4. 228
    5. 6 你好
    6. 6 你好
    7. 你好,世界

    2.2.5 Buffer 类型

    1. type Buffer struct {
    2. buf []byte
    3. off int
    4. lastRead readOp
    5. }

    在上一个示例的最后,我们使用了 bytes.Buffer 类型,该类型实现了 io 包下的 ByteScanner, ByteWriter, ReadWriter, Reader, ReaderFrom, RuneReader, RuneScanner, StringWriter, Writer, WriterTo 等接口,可以方便的进行读写操作。

    对象可读取数据为 buf[off : len(buf)], off 表示进度下标,lastRead 表示最后读取的一个字符所占字节数,方便 Unread* 相关操作。

    Buffer 可以通过 3 中方法初始化对象:

    1. a := bytes.NewBufferString("Hello World")
    2. b := bytes.NewBuffer([]byte("Hello World"))
    3. c := bytes.Buffer{}
    4. fmt.Println(a)
    5. fmt.Println(b)
    6. fmt.Println(c)
    7. }

    输出结果:

    1. Hello World
    2. Hello World
    3. {[] 0 0}

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

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

    示例:

    1. Good Night
    2. 10
    3. delim:N err: EOF

    其它大部分函数、方法与 strings 包下的函数、方法类似,只是数据源从 string 变为了 []byte ,请参考 strings 包的用法。

    导航

    • 下一节: