go基础——字节

2020-10-04  本文已影响0人  chase_lwf

内容

一 byte/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基本操作

将240第四位设置为1,步骤:1先左移3位,得到1000; 然后与原来值或运算
    a := uint8(240)
    fmt.Printf("%b\n", a)
    a = a | (1 << 3)
    fmt.Printf("%b\n", a)
输出:
11110000
11111000
将第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

三 字节序

字节序通俗来说,就是多字节数据类型在内存中的存放顺序,有两种顺序:

数值本身低位在右边,由右向左,即:低位-》高位;
在实际场景中,字节序有两种:

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)
    }
}

引用:

上一篇下一篇

猜你喜欢

热点阅读