背景
- 最近在使用GO写业务,空闲时看看三方库和标准库的一些工具的实现。
理论
- 底层数据都是按字节为单位存储。
- ASCII码取值区间0-127,二进制表示为0b00000000-0b01111111。
- UTF8编码方式:根据具体字符情况,使用1-4字节长度存储数据。
- 1字节:用ASCII能表示的字符,直接使用ASCII码表示,占用1字节。
- 多(2-4)字节:首字节数据的高3位-高5位,用于表示当前字符占用的字节数量,剩余位存储具体数据。其他字节数据的最高2位必须是1和0,剩余位存储具体数据。
UTF8数据存储举例
- "界"字在UTF8编码中占用3字节,表示方式为:
- 首字节:0b11100111(高4位的1110为数据长度表示,前三位的1表示占用3字节,第4位的0为分隔符,后面的0111为首字节存储的实际数据)
- 二字节:0b10010101(高2位的10为非首字符的固定表示方式,用于区别于ASCII码数据。后面的6位010101为存储的实际数据)
- 三字节:0b10001100(与二字节同理)
编码
// EncodeRune writes into p (which must be large enough) the UTF-8 encoding of the rune. // If the rune is out of range, it writes the encoding of RuneError. // It returns the number of bytes written. func EncodeRune(p []byte, r rune) int { // Negative values are erroneous. Making it unsigned addresses the problem. switch i := uint32(r); { // rune1Max的值为127,小于等于这个数的字符,可以直接使用1字节的ASCII码表示 case i <= rune1Max: p[0] = byte(r) return 1 // rune2Max的值为2047,小于等于这个数的字符,可以用2字节存储 case i <= rune2Max: _ = p[1] // eliminate bounds checks // t2的值为0b11000000,使得首字符的高2位必定为11 p[0] = t2 | byte(r>>6) p[1] = tx | byte(r)&maskx return 2 case i > MaxRune, surrogateMin <= i && i <= surrogateMax: r = RuneError fallthrough // rune3Max的值为65535,小于等于这个数的字符,可以用3字节存储 case i <= rune3Max: _ = p[2] // eliminate bounds checks // t3的值为0b11100000,使得首字符的高3位必定为111 p[0] = t3 | byte(r>>12) // t1的值为0b10000000 p[1] = tx | byte(r>>6)&maskx p[2] = tx | byte(r)&maskx return 3 // 其他的用4字节存储 default: _ = p[3] // eliminate bounds checks // t4的值为0b11110000,使得首字符的高4位必定为1111 p[0] = t4 | byte(r>>18) p[1] = tx | byte(r>>12)&maskx p[2] = tx | byte(r>>6)&maskx p[3] = tx | byte(r)&maskx return 4 } }
- 该方法根据字符数据的不同大小,决定使用多少字节的空间进行编码和存储。编码过程中,首字节数据的高位会根据不同字节长度,设置其高位1的个数。由于数据的区间大小及每字节最多存储6位实际数据的原因,在高位后面必然有一位位0,用于表示高位的结束和实际数据的开始。
解码
// DecodeRune unpacks the first UTF-8 encoding in p and returns the rune and // its width in bytes. If p is empty it returns (RuneError, 0). Otherwise, if // the encoding is invalid, it returns (RuneError, 1). Both are impossible // results for correct, non-empty UTF-8. // // An encoding is invalid if it is incorrect UTF-8, encodes a rune that is // out of range, or is not the shortest possible UTF-8 encoding for the // value. No other validation is performed. func DecodeRune(p []byte) (r rune, size int) { n := len(p) if n < 1 { return RuneError, 0 } // 取出首字节 p0 := p[0] // 根据首位的值,取出长度的表示数据 x := first[p0] if x >= as { // The following code simulates an additional check for x == xx and // handling the ASCII and invalid cases accordingly. This mask-and-or // approach prevents an additional branch. mask := rune(x) << 31 >> 31 // Create 0x0000 or 0xFFFF. return rune(p[0])&^mask | RuneError&mask, 1 } sz := int(x & 7) accept := acceptRanges[x>>4] if n < sz { return RuneError, 1 } b1 := p[1] if b1 < accept.lo || accept.hi < b1 { return RuneError, 1 } if sz <= 2 { // <= instead of == to help the compiler eliminate some bounds checks return rune(p0&mask2)<<6 | rune(b1&maskx), 2 } b2 := p[2] if b2 < locb || hicb < b2 { return RuneError, 1 } if sz <= 3 { return rune(p0&mask3)<<12 | rune(b1&maskx)<<6 | rune(b2&maskx), 3 } b3 := p[3] if b3 < locb || hicb < b3 { return RuneError, 1 } return rune(p0&mask4)<<18 | rune(b1&maskx)<<12 | rune(b2&maskx)<<6 | rune(b3&maskx), 4 }
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。