美文网首页
go基础——字节

go基础——字节

作者: chase_lwf | 来源:发表于2020-10-04 20:18 被阅读0次

    内容

    • 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://www.haomeiwen.com/subject/wacwuktx.html