go——字节

酷酷码农 · · 1388 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

内容

  • 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

二 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——字节

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1388 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传