內(nèi)容
- 1 byte/rune
- 2 bit基本操作
- 3 字節(jié)序
- 4 一個(gè)bit使用例子——bitmap
一 byte/rune
- 編碼: 編碼就是人類語(yǔ)言字符和存儲(chǔ)中計(jì)算機(jī)中的字節(jié)的一種映射表泛烙,最開始是用ascii編碼表就可以表示完所有的英文字符,但是沒法表示其他語(yǔ)言字符贬丛,所以誕生了unicode版扩、utf-8等不同的編碼表旦部;go中用byte和rune類型來代表ascii字符和unicode字符塞弊;
- byte和uint8是等價(jià)的蔫缸,通常處理ascii字符惋鸥,因?yàn)橹挥?位來存儲(chǔ)猬腰,實(shí)際只可以存儲(chǔ)256個(gè)字符鸟废,所以有了unicode、utf-8等編碼來處理其他復(fù)雜字符
- go中的rune是int32的一個(gè)別名姑荷,占用4個(gè)字節(jié)存儲(chǔ)盒延,一個(gè)rune就是一個(gè)unicode字符,當(dāng)代碼中要處理中文鼠冕、日文等字符時(shí)添寺,通常使用rune來處理。
所以在一些場(chǎng)景就要特別注意:
1 計(jì)算中文字符串長(zhǎng)度不用直接用len, 字符串的實(shí)現(xiàn)是一個(gè)byte[], len求的是字節(jié)數(shù)組的長(zhǎng)度懈费,s3在存儲(chǔ)時(shí)實(shí)際是存儲(chǔ)成一個(gè)字節(jié)數(shù)組计露,共6個(gè)字節(jié),代替應(yīng)該用utf8.RuneCountInString()來統(tǒng)計(jì)長(zhǎng)度楞捂,應(yīng)該統(tǒng)計(jì)中文字符unicode編碼后的rune長(zhǎng)度才合適
s := '中' // 用單引號(hào)代表一個(gè)字符薄坏,要和雙引號(hào)區(qū)別面徽,注意和python里區(qū)別
var s2 byte = 'a'
s3 := "中國(guó)"
fmt.Println(unsafe.Sizeof(s))
fmt.Println(unsafe.Sizeof(s2))
fmt.Println(len(s3))
fmt.Println(utf8.RuneCountInString(s3))
輸出:
4
1
6
2
補(bǔ)充:
1個(gè)字節(jié)byte(8位2進(jìn)制數(shù))戴卜,可以用來表示1個(gè)[0-255]之間的10進(jìn)制數(shù),可以用來表示2個(gè)16進(jìn)制數(shù)(每四位二進(jìn)制可以表示1個(gè)16進(jìn)制數(shù)), 16進(jìn)制數(shù)以0x開頭叔壤,0xff轉(zhuǎn)換二級(jí)制是1111111繁堡,所以有些代碼里經(jīng)成蛏疲看到0xff,0xff等價(jià)于11111111椭蹄,使用0xff看起來更加直觀,簡(jiǎn)潔
二 bit基本操作
- 將字節(jié)中某位設(shè)置為1
將240第四位設(shè)置為1闻牡,步驟:1先左移3位,得到1000绳矩; 然后與原來值或運(yùn)算
a := uint8(240)
fmt.Printf("%b\n", a)
a = a | (1 << 3)
fmt.Printf("%b\n", a)
輸出:
11110000
11111000
- 將某一位設(shè)置為0
將第7位設(shè)置為0罩润, 步驟:1左移6位,然后取反翼馆,然后與運(yùn)算
a := uint8(240)
fmt.Printf("%b\n", a)
a = a&^(1 << 6)
fmt.Printf("%b\n", a)
輸出:
11110000
10110000
- 獲取某一位的值
獲取第5位的值:先將左移兩位割以,將第5位的值頂?shù)阶铐敹耍缓笤谟乙?位应媚,將第5位的值移到最右端严沥,邊得到第5位的值,如果是uint16 uint32的則相應(yīng)的根據(jù)類型總位數(shù)計(jì)算一下中姜,類似uint8 8位的計(jì)算
a := uint8(240)
fmt.Printf("%b\n", a)
a = (a<<2)>>7
fmt.Printf("%b\n", a)
輸出:
11110000
1
三 字節(jié)序
字節(jié)序通俗來說消玄,就是多字節(jié)數(shù)據(jù)類型在內(nèi)存中的存放順序,有兩種順序:
- 大端序:數(shù)值的低位存放在地址的高位,高位存放在低地址
-
小端序:數(shù)值的低位存放在低地址翩瓜,高位存放在高地址受扳;
例如:
數(shù)值本身低位在右邊,由右向左奥溺,即:低位-》高位辞色;
在實(shí)際場(chǎng)景中,字節(jié)序有兩種:
- 網(wǎng)絡(luò)字節(jié)序:字節(jié)在網(wǎng)絡(luò)中傳輸?shù)木幣彭樞蚋《ǎ趖cp/ip協(xié)議中相满,使用的是大端序;
- 主機(jī)字節(jié)序: 數(shù)值在內(nèi)存中的存儲(chǔ)順序桦卒,不同cpu使用方式不同立美,有的是大端序,有的是小端序方灾,普遍的是小端序建蹄;
各自優(yōu)勢(shì): - Big Endian:符號(hào)位的判定固定為第一個(gè)字節(jié),容易判斷正負(fù)裕偿。
- Little Endian:長(zhǎng)度為1洞慎,2,4字節(jié)的數(shù)嘿棘,排列方式都是一樣的劲腿,數(shù)據(jù)類型轉(zhuǎn)換非常方便。小字節(jié)類型轉(zhuǎn)換成大字節(jié)鸟妙,因?yàn)轫樞蛞恢陆谷耍恍枰诤竺婕?即可;
所以當(dāng)從網(wǎng)絡(luò)中接收字節(jié)數(shù)據(jù)后需要轉(zhuǎn)換成小端序后重父,再對(duì)數(shù)據(jù)進(jìn)行處理花椭。
Go中在encoding/binary 包中的全局變量BigEndian用于操作大端序數(shù)據(jù),LittleEndian用于操作小端序數(shù)據(jù)房午,這兩個(gè)變量所對(duì)應(yīng)的數(shù)據(jù)類型都實(shí)行了ByteOrder接口:
type ByteOrder interface {
// 用于讀取
Uint16([]byte) uint16
Uint32([]byte) uint32
Uint64([]byte) uint64
// 用于寫入
PutUint16([]byte, uint16)
PutUint32([]byte, uint32)
PutUint64([]byte, uint64)
String() string
}
看個(gè)具體例子:
//判斷系統(tǒng)中的字節(jié)序類型
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實(shí)現(xiàn)簡(jiǎn)單bitmap
位運(yùn)算的一個(gè)使用場(chǎng)景矿辽,構(gòu)造一個(gè)位圖
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
}
// 獲取在字節(jié)數(shù)組中下標(biāo),num除以8 就得到num在數(shù)組中的位置
// num / 8 == num >> 3
func (bm *bitMap) GetIndex(num uint) uint {
return num >> 3
}
// 獲取在一個(gè)字節(jié)中的位的位置郭厌,byte[index]中的第幾位
// num % 8得到在一個(gè)字節(jié)中的位置
func (bm *bitMap) GetPosition(num uint) uint {
return num & (8 - 1)
}
// 標(biāo)記指定數(shù)字(num)在bitmap中的值袋倔,標(biāo)記其已經(jīng)出現(xiàn)過
// 將1左移position后,那個(gè)位置自然就是1沪曙,然后和以前的數(shù)據(jù)做或運(yùn)算,這樣萎羔,那個(gè)位置就替換成1了
func (bm *bitMap) Add(num uint) {
index := bm.GetIndex(num)
bm.data[index] |= 1 << bm.GetPosition(num)
}
// 判斷num是否在位圖中
// 將1左移position位置后液走,那個(gè)位置自然就是1,然后和以前的數(shù)據(jù)做與運(yùn)算,判斷是否為1
// 如果結(jié)果為1缘眶,則以前那個(gè)位置就是1嘱根,否則以前的那個(gè)位置的數(shù)是0
func (bm *bitMap) Contains(num uint) bool {
index := bm.GetIndex(num)
return bm.data[index]&1<<bm.GetPosition(num) == 1
}
// 打印byte類型變量
// 把byte轉(zhuǎn)換為一個(gè)長(zhǎng)度為8的數(shù)組,數(shù)組每個(gè)值代表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語(yǔ)言字節(jié)序:https://lihaoquan.me/2016/11/5/golang-byteorder.html
- 字節(jié)序:Big Endian 和 Little Endian:https://songlee24.github.io/2015/05/02/endianess/