首页 > 其他分享 >实现对象转成字节数组(整型支持按位写入,字符串则按字节写入)

实现对象转成字节数组(整型支持按位写入,字符串则按字节写入)

时间:2024-01-23 18:12:51浏览次数:47  
标签:字节 int 写入 按位 user offset public size

闲着无聊,写了一个对象转换成byte[]的工具类,支持整型按位写入(大大节省空间),具体步骤如下:

1. 定义实体类和注解

public class User {

    /**
     * ID,4个字节,32bit
     */
    @JSONField(ordinal = 1)
    @BitPos(offset=0,size = 32)
    public int id;

    /**
     * 姓名,10个字节(80bit)
     */
    @JSONField(ordinal = 2)
    @BitPos(offset = 32, size= 80)
    public String name;

    /**
     * 性别,0:男,1:女,1Bit
     */
    @JSONField(ordinal = 3)
    @BitPos(offset = 112, size = 1)
    public int sex;

    /**
     * 年龄,最大127,7Bit
     */
    @JSONField(ordinal = 4)
    @BitPos(offset = 113, size=7)
    public int age;

    /**
     * 身高,最大2^10-1=1023cm,10Bit
     */
    @JSONField(ordinal = 5)
    @BitPos(offset = 120, size = 10)
    public int height;

    /**
     * 体重,最大2^10-1=1023kg,10Bit
     */
    @JSONField(ordinal = 6)
    @BitPos(offset = 130, size = 10)
    public int weight;

    /**
     * 多少个月的薪水,最大2^4-1=15个月薪,4Bit
     */
    @JSONField(ordinal = 7)
    @BitPos(offset = 140, size=4)
    public int monthSalary;

    /**
     * 地址:20字节,160bit
     */
    @JSONField(ordinal = 8)
    @BitPos(offset = 144, size = 160)
    public String address;


}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BitPos {

    /**
     * 位置(占总长度的位置)
     */
    int offset() default  -1;

    /**
     * 长度(多少bit)
     */
    int size() default  -1;
}

2. 工具类

/**
 * 对象转位字节(整型实现按bit写入,字符串则按字节写入)
 *
 * 时间: 2024/1/23 11:12
 */
public class ObjToBitBytesUtil {

    public static void main(String[] args) throws IllegalAccessException, UnsupportedEncodingException {
        User user = new User();
        user.id = 10001;
        user.name = "张三";
        user.sex = 0;
        user.age = 18;
        user.height = 170;
        user.weight = 50;
        user.monthSalary = 13;
        user.address = "浙江杭州西湖";

        System.out.println("原始对象:");
        System.out.println(JSON.toJSONString(user, SerializerFeature.WriteNullStringAsEmpty));

        //对象写成字节数组
        byte[] bytes = writeObjToBitBytes(user, 38);
        System.out.println("对象的字节数组16进制表示:");
        printHex(bytes);


        //字节数组转成对象
        User readUser = readBitBytesToObj(bytes);
        System.out.println("字节数组转换成的对象:");
        System.out.println(JSON.toJSONString(readUser,SerializerFeature.WriteNullStringAsEmpty));
    }

    private static User readBitBytesToObj(byte[] bytes) throws IllegalAccessException, UnsupportedEncodingException {
        User user = new User();
        Field[] fields = user.getClass().getFields();
        for (Field field : fields) {
            BitPos bitPos = field.getAnnotation(BitPos.class);
            Object val = readField(bytes, field, bitPos.offset(), bitPos.size());
            field.set(user,val);
        }
        return user;
    }

    private static Object readField(byte[] buffer, Field field, int offset, int size) throws UnsupportedEncodingException {
        Object val = null;
       if(field.getType().equals(int.class) || field.getType().equals(Integer.class)){
           // 整型,按位读取
           int valInt = 0;

           //起始缓存位置(第几个字节,从0开始)
           int startBufferIndex = offset / 8;
           //起始字节已经占用了多少bit
           int startByteUsedBit = offset % 8;
           int startByteRemainBit = 8 - startByteUsedBit;
           //结束缓存位置(第几个字节)
           int endBufferIndex = (offset + size - 1) / 8;
           int endByteUseBit = ((offset + size - 1) % 8)+1;
           // 缓存间隔位置(缓存起止位置之间的间隔字节数)
           int gapByteCount = endBufferIndex - startBufferIndex - 1;

           // 1. 读取起始字节(读高位)
           byte lowerByte = buffer[startBufferIndex];
           lowerByte = (byte) (lowerByte >>> startByteUsedBit);
           int mask = (1 << startByteRemainBit) - 1;
           valInt = lowerByte & mask;

           // 2. 读取中间字节(读整个字节)
           if(gapByteCount > 0) {
               for (int i = 0; i < gapByteCount; i++) {
                   int leftMove = startByteRemainBit + (i * 8);
                   byte b = buffer[startBufferIndex+(i+1)];
                   valInt |= (b << leftMove);
               }
           }

           // 3. 读取结束字节(读取低位)
           if(endBufferIndex > startBufferIndex) {
               byte b = buffer[endBufferIndex];
               int leftMove = startByteRemainBit + gapByteCount * 8;
               mask = (1 << endByteUseBit) - 1;
               valInt |= ((b & mask) << leftMove);
           }
           val = valInt;
       }else{
           // 字符串按字节读取
           byte[] bytes = new byte[size/8];
           int startBufferIndex = offset / 8;
           for(int i=0;i< bytes.length;i++){
               bytes[i] = buffer[startBufferIndex++];
           }
           String valStr = new String(bytes,"utf-8");
           val = valStr;
       }
       return val;
    }

    private static byte[] writeObjToBitBytes(User user, int bufferSize) throws IllegalAccessException, UnsupportedEncodingException {
        byte[] buffer = new byte[bufferSize];
        Field[] fields = user.getClass().getFields();
        for (Field field : fields) {
            BitPos bitPos = field.getAnnotation(BitPos.class);
            Object val = field.get(user);
            writeField(buffer, val, bitPos.offset(), bitPos.size());
        }
        return buffer;
    }

    private static void writeField(byte[] buffer, Object val, int offset, int size) throws UnsupportedEncodingException {
        if (val instanceof Integer) {
            // 整型,按位写入
            int valInt = (int) val;

            //起始缓存位置(第几个字节,从0开始)
            int startBufferIndex = offset / 8;
            //起始字节已经占用了多少bit
            int startByteUsedBit = offset % 8;
            int startByteRemainBit = 8 - startByteUsedBit;
            //结束缓存位置(第几个字节)
            int endBufferIndex = (offset + size - 1) / 8;
            int endByteUseBit = ((offset + size - 1) % 8)+1;
            // 缓存间隔位置(缓存起止位置之间的间隔字节数)
            int gapByteCount = endBufferIndex - startBufferIndex - 1;

            // 1. 写入起始字节(之前在低位可能已经存在值了,现在写入的写到高位去)
            int mask = (1 << startByteRemainBit) - 1;
            byte lowerByte = (byte) (valInt & mask);
            //取低startByteRemainBit位,则左移startByteUsedBit位,空出来的与原来的值或操作即可
            buffer[startBufferIndex] |= ((lowerByte & 0xFF) << startByteUsedBit);

            // 2. 写中间字节(全字节写入)
            if(gapByteCount > 0) {
                for (int i = 0; i < gapByteCount; i++) {
                    int rightMove = startByteRemainBit + (i * 8);
                    buffer[startBufferIndex+(i+1)] = (byte) ((valInt >> rightMove) & 0xFF);
                }
            }

            // 3. 写结束字节(写入低位即可)
            if(endBufferIndex > startBufferIndex) {
                int rightMove = startByteRemainBit + gapByteCount * 8;
                mask = (1 << endByteUseBit) - 1;
                buffer[endBufferIndex] = (byte) ((valInt >> rightMove) & mask);
            }
        } else {
            // 字符串直接按字节写入
            byte[] bytes = val.toString().getBytes("utf-8");
            int actualByteCount = bytes.length;
            int startBufferIndex = offset / 8;
            int endBufferIndex = startBufferIndex + size/8-1;
            int byteIndex = 0;
            for(int i = startBufferIndex; i<=endBufferIndex; i++) {
                if(byteIndex <= actualByteCount-1) {
                    buffer[i] = bytes[byteIndex++];
                }else{
                    //补空格
                    buffer[i] = ' ';
                }
            }
        }

    }


    private static void printHex(byte[] byteArray) {
        for (byte b : byteArray) {
            // 将每个字节转换为16进制字符串
            String hex = String.format("%02X", b);
            System.out.print(hex + " ");
        }
        System.out.println();
    }
}

3. 测试结果

原始对象:
{"id":10001,"name":"张三","sex":0,"age":18,"height":170,"weight":50,"monthSalary":13,"address":"浙江杭州西湖"}
对象的字节数组16进制表示:
11 27 00 00 E5 BC A0 E4 B8 89 20 20 20 20 24 AA C8 D0 E6 B5 99 E6 B1 9F E6 9D AD E5 B7 9E E8 A5 BF E6 B9 96 20 20 
字节数组转换成的对象:
{"id":10001,"name":"张三    ","sex":36,"age":18,"height":170,"weight":50,"monthSalary":13,"address":"浙江杭州西湖  "}

 

参考文章:

https://www.cnblogs.com/Dotnet9-com/p/17981055

标签:字节,int,写入,按位,user,offset,public,size
From: https://www.cnblogs.com/hdwang/p/17983063

相关文章

  • Go语言核心36讲 38 | bytes包与字节串操作(上)
    我相信,经过上一次的学习,你已经对strings.Builder和strings.Reader这两个类型足够熟悉了。我上次还建议你去自行查阅strings代码包中的其他程序实体。如果你认真去看了,那么肯定会对我们今天要讨论的bytes代码包,有种似曾相识的感觉。前导内容:bytes.Buffer基础知识strings包和b......
  • 字节面试:索引的设计规范,你知道多少?
    小北说在前面:在一线互联网企业种,如网易、美团、字节、如阿里、滴滴、极兔、有赞、希音、百度、美团等大厂,数据库的面试题,一直是核心和重点的提问点,比如前段时间有位小伙伴面试字节,就遇到了下面这道面试题:索引的设计规范,你知道那些?小伙伴虽然用过索引,但是索引的设计规范忘记......
  • 92年字节女年薪400万,评论区看得我真是笑死!
    92年字节女年薪400万,评论区看得我真是笑死。这其实是去年的一篇帖子了,听说在知乎上关于男女拳话题,流量奇高!哈哈哈,正好试试!......
  • 【APP逆向07】字符串与字节的转换
    1.逆向的时候,很多时候字符串都是通过字节来表示的importjava.util.Arrays;publicclassHello{publicstaticvoidmain(String[]args){//1.字节数组(转换为字符串)[字节,字节,字节]byte[]dataList={97,105,100,61,50,52,54,51,56,5......
  • STM32系统开发--位、字节操作
    针对MCU的嵌入是开发中经常涉及到寄存器的操作,例如GPIO配置低寄存器GPIOx_CRL(共32个bit),有时需要改变其中的一位或者几位bit值,同时不能影响其它bit位的值。 例如,需要设置第0位bit=1时,不能简单的设置为:GPIOx_CRL=0x01,这样的方法会使得低配置寄存器GPIOx_CRL的其它比特位都置......
  • 字节需要新的“大力”
    来源|微信公众号“听筒Tech”作者|听筒Tech工作室崇尚“大力出奇迹”的字节似乎正在改变这一策略。在过去的一段时间里,字节一改过去的激进,不再奋力秀肌肉,而是开始做“减法”。 1月10日,有消息称,字节跳动将拆分旗下懂车帝成为独立公司,随后,字节证实此事,称注册成立独立公司是基于......
  • C# 将字节数组,数值和十六进制字符串相互转换
    byte[]bs=newbyte[32];Randomrandom=newRandom();random.NextBytes(bs);//给字节数组填充随字节stringhex=BitConverter.ToString(bs);//将字节数组转成十六进制字符串,默认-分割Console.WriteLi......
  • 字节跳动今年的校招薪资以及面试真题
    字节跳动校招情况分析在写完了绝对顶流华为和近两年炙手可热的比亚迪的校招薪资之后,不少同学点名要看「字节跳动」。确实,玩归玩,闹归闹,别拿字节开玩笑。先来看看和公众号读者相关性较高的岗位校待遇:研发算法签字费白菜20k~23k22k~25k1wSP24k~27k26k~29k1wSSP28k~33k30k~36k1w字......
  • 一道字节的 TS 体操面试真题
    前天,小册群友问了我一个TS体操问题,说是面字节时遇到的。今天又催了一下:面试题是这样的:让实现这个FormatDate的类型,用来限制字符串只能是指定的日期格式。看起来好像没多大难度,就是提取出YY、MM、DD和分隔符,然后构造对应的字符串类型就好了。但上手试了一下,还真没那么简单。首......
  • 使用 Python 将数据写入 Excel 工作表
    在数据处理和报告生成等工作中,Excel表格是一种常见且广泛使用的工具。然而,手动将大量数据输入到Excel表格中既费时又容易出错。为了提高效率并减少错误,使用Python编程语言来自动化数据写入Excel表格是一个明智的选择。Python作为一种简单易学且功能强大的编程语言,其丰富的......