内容
- 1 byte/rune
- 2 bit基本操作
- 3 字节序
- 4 一个bit使用例子——bitmap
一 byte/rune
- 编码: 编码就是人类语言字符和存储中计算机中的字节的一种映射表,最开始是用ascii编码表就可以表示完所有的英文字符,但是没法表示其他语言字符,所以诞生了unicode、utf-8等不同的编码表;go中用byte和rune类型来代表ascii字符和unicode字符;
- byte和uint8是等价的,通常处理ascii字符,因为只用8位来存储,实际只可以存储256个字符,所以有了unicode、utf-8等编码来处理其他复杂字符
- go中的rune是int32的一个别名,占用4个字节存储,一个rune就是一个unicode字符,当代码中要处理中文、日文等字符时,通常使用rune来处理。
所以在一些场景就要特别注意:
1 计算中文字符串长度不用直接用len, 字符串的实现是一个byte[], len求的是字节数组的长度,s3在存储时实际是存储成一个字节数组,共6个字节,代替应该用utf8.RuneCountInString()来统计长度,应该统计中文字符unicode编码后的rune长度才合适
s := '中' // 用单引号代表一个字符,要和双引号区别,注意和python里区别
var s2 byte = 'a'
s3 := "中国"
fmt.Println(unsafe.Sizeof(s))
fmt.Println(unsafe.Sizeof(s2))
fmt.Println(len(s3))
fmt.Println(utf8.RuneCountInString(s3))
输出:
4
1
6
2
补充:
1个字节byte(8位2进制数),可以用来表示1个[0-255]之间的10进制数,可以用来表示2个16进制数(每四位二进制可以表示1个16进制数), 16进制数以0x开头,0xff转换二级制是1111111,所以有些代码里经常看到0xff,0xff等价于11111111,使用0xff看起来更加直观,简洁
二 bit基本操作
- 将字节中某位设置为1
将240第四位设置为1,步骤:1先左移3位,得到1000; 然后与原来值或运算
a := uint8(240)
fmt.Printf("%b\n", a)
a = a | (1 << 3)
fmt.Printf("%b\n", a)
输出:
11110000
11111000
- 将某一位设置为0
将第7位设置为0, 步骤:1左移6位,然后取反,然后与运算
a := uint8(240)
fmt.Printf("%b\n", a)
a = a&^(1 << 6)
fmt.Printf("%b\n", a)
输出:
11110000
10110000
- 获取某一位的值
获取第5位的值:先将左移两位,将第5位的值顶到最顶端,然后在右移7位,将第5位的值移到最右端,边得到第5位的值,如果是uint16 uint32的则相应的根据类型总位数计算一下,类似uint8 8位的计算
a := uint8(240)
fmt.Printf("%b\n", a)
a = (a<<2)>>7
fmt.Printf("%b\n", a)
输出:
11110000
1
三 字节序
字节序通俗来说,就是多字节数据类型在内存中的存放顺序,有两种顺序:
- 大端序:数值的低位存放在地址的高位,高位存放在低地址
-
小端序:数值的低位存放在低地址,高位存放在高地址;
例如:
image.png
数值本身低位在右边,由右向左,即:低位-》高位;
在实际场景中,字节序有两种:
- 网络字节序:字节在网络中传输的编排顺序,在tcp/ip协议中,使用的是大端序;
- 主机字节序: 数值在内存中的存储顺序,不同cpu使用方式不同,有的是大端序,有的是小端序,普遍的是小端序;
各自优势: - Big Endian:符号位的判定固定为第一个字节,容易判断正负。
- Little Endian:长度为1,2,4字节的数,排列方式都是一样的,数据类型转换非常方便。小字节类型转换成大字节,因为顺序一致,只需要在后面加0即可;
所以当从网络中接收字节数据后需要转换成小端序后,再对数据进行处理。
Go中在encoding/binary 包中的全局变量BigEndian用于操作大端序数据,LittleEndian用于操作小端序数据,这两个变量所对应的数据类型都实行了ByteOrder接口:
type ByteOrder interface {
// 用于读取
Uint16([]byte) uint16
Uint32([]byte) uint32
Uint64([]byte) uint64
// 用于写入
PutUint16([]byte, uint16)
PutUint32([]byte, uint32)
PutUint64([]byte, uint64)
String() string
}
看个具体例子:
//判断系统中的字节序类型
func systemEdian() {
var i int = 0x1
bs := (*[INT_SIZE]byte)(unsafe.Pointer(&i))
if bs[0] == 0 {
fmt.Println("system edian is little endian")
} else {
fmt.Println("system edian is big endian")
}
}
func testBigEndian() {
//00000000 00000000 00000000 00001111
var testInt uint32 = 15
fmt.Printf("%d use big endian: \n", testInt)
var testBytes []byte = make([]byte, 4)
binary.BigEndian.PutUint32(testBytes, testInt)
fmt.Println("int32 to bytes:", testBytes)
convInt := binary.BigEndian.Uint32(testBytes)
fmt.Printf("bytes to int32: %d\n\n", convInt)
}
func testLittleEndian() {
//00000000 00000000 00000000 00001111
var testInt uint32 = 15
fmt.Printf("%d use little endian: \n", testInt)
var testBytes []byte = make([]byte, 4)
binary.LittleEndian.PutUint32(testBytes, testInt)
fmt.Println("int32 to bytes:", testBytes)
convInt := binary.LittleEndian.Uint32(testBytes)
fmt.Printf("bytes to int32: %d\n\n", convInt)
}
输出:
system edian is big endian
15 use big endian:
int32 to bytes: [0 0 0 15]
bytes to int32: 15
15 use little endian:
int32 to bytes: [15 0 0 0]
bytes to int32: 15
四 go实现简单bitmap
位运算的一个使用场景,构造一个位图
package main
import "fmt"
type bitMap struct {
data []byte
size int
}
func NewBitMap(size int) *bitMap {
bm := &bitMap{}
if size > 0 {
bm.size = size
}
return bm
}
// 获取在字节数组中下标,num除以8 就得到num在数组中的位置
// num / 8 == num >> 3
func (bm *bitMap) GetIndex(num uint) uint {
return num >> 3
}
// 获取在一个字节中的位的位置,byte[index]中的第几位
// num % 8得到在一个字节中的位置
func (bm *bitMap) GetPosition(num uint) uint {
return num & (8 - 1)
}
// 标记指定数字(num)在bitmap中的值,标记其已经出现过
// 将1左移position后,那个位置自然就是1,然后和以前的数据做或运算,这样,那个位置就替换成1了
func (bm *bitMap) Add(num uint) {
index := bm.GetIndex(num)
bm.data[index] |= 1 << bm.GetPosition(num)
}
// 判断num是否在位图中
// 将1左移position位置后,那个位置自然就是1,然后和以前的数据做与运算,判断是否为1
// 如果结果为1,则以前那个位置就是1,否则以前的那个位置的数是0
func (bm *bitMap) Contains(num uint) bool {
index := bm.GetIndex(num)
return bm.data[index]&1<<bm.GetPosition(num) == 1
}
// 打印byte类型变量
// 把byte转换为一个长度为8的数组,数组每个值代表bit
func (bm *bitMap) ShowByte(b byte) {
array := make([]byte, 8)
for i := 0; i < 8; i++ {
array[i] = b & 1
b = b >> 1
}
for _, b1 := range array {
fmt.Println(b1)
}
}
引用:
- go语言字节序:https://lihaoquan.me/2016/11/5/golang-byteorder.html
- 字节序:Big Endian 和 Little Endian:https://songlee24.github.io/2015/05/02/endianess/
网友评论