JAVA实现类似C语言联合体般 充分使用bit位存储数据的方法之一

        本人android码农一个,这几天需要对接一个服务器,使用的是GTP协议,看了一下协议,顿时觉得蒙逼了。因为存储数据节约到了bit位,作为用惯了JAVA的人,顿时觉得这是得有多抠啊,不过由于我跟服务器用的是长连接,数据交互很频繁,所以这样其实大大节省了数据量,对于使用GPRS流量的用户是很有必要的。

        对接中我遇到的第一个问题就是数据封装和解析都是按bit位算的,一个数据占多少个bit这样的。现在遇到的问题就比如说,现在用两个byte来存CTRL,三个bit用来存版本号,一个bit用来做判断是否需要ACK,三个bit用来存优先级,9个bit用来存报文序号。对于C语言来说,这没什么,定义一个联合体就行了,但是对于java来说,就有点晕了,因为平常我们用到的最小的是byte,要这样操作bit位的话,我们需要一大堆位运算,想想就很可怕。虽然可以用C写,然后用JNI调用,但是不甘心我的还是想用java来更简单得实现。终于在洗澡的时候让我想到了,用string来实现。

       先说说原理,就是把一个string对象当成一块内存,里面存着一串bit,然后对里面的bit进行操作,最后再把这堆bit封装成byte。

       现在就以  用两个byte来存CTRL,三个bit用来存版本号,一个bit用来做判断是否需要ACK,三个bit用来存优先级,9个bit用来存报文序号   这个需求来实现吧。

      1、先封装三个方法一个是把int转成String格式的bit串,一个是string格式的bit串转为byte,一个是设定好bit串长度 。

/** 
     * 将byte转成string格式bit串并省略前面的0
     */  
    public static String byteToBitFS(byte b) {  
    	boolean hn = false;
    	byte[] bits = new byte[8];
    	bits[0] = (byte)((b >> 7) & 0x1);
    	bits[1] = (byte)((b >> 6) & 0x1);
    	bits[2] = (byte)((b >> 5) & 0x1);
    	bits[3] = (byte)((b >> 4) & 0x1);
    	bits[4] = (byte)((b >> 3) & 0x1);
    	bits[5] = (byte)((b >> 2) & 0x1);
    	bits[6] = (byte)((b >> 1) & 0x1);
    	bits[7] = (byte)((b >> 0) & 0x1);
    	StringBuffer sb = new StringBuffer();
    	for(byte temp : bits){
    		if(hn){
    			sb.append(temp);	
    		}else{
    			if(temp != 0x00){
    				sb.append(temp);	
    				hn=true;
        			
        		}
    		}
    		
    	}
    	
        return sb.toString();  
    } 
    
    /** 
     * 将string格式的bit串转成byte 
     */  
    public static byte BitToByte(String byteStr) {  
        int re, len;  
        if (null == byteStr) {  
            return 0;  
        }  
        len = byteStr.length();  
        if (len != 4 && len != 8) {  
            return 0;  
        }  
        if (len == 8) { 
            if (byteStr.charAt(0) == '0') {
                re = Integer.parseInt(byteStr, 2);  
            } else {
                re = Integer.parseInt(byteStr, 2) - 256;  
            }  
        } else {
            re = Integer.parseInt(byteStr, 2);  
        }  
        return (byte) re;  
    }  
/**
	 * 检查bit是长度是否足够,不够前面添0,超出返回null
	 */
	public static String resetBitStrand(String str, int length) {
		if (str.length() > length) {
			return null;
		}
		StringBuffer sb = new StringBuffer();
		if (str.length() < length) {
			for (int i = 0; i < length - str.length(); i++) {
				sb.append("0");
			}
		}
		sb.append(str);
		return sb.toString();


	}
      2、先把2个byte也就是16个bit的位置准备好,开始添加数据

public static void setCTRL(byte version, boolean needack, byte priority,
			int sno) {
		StringBuffer sb = new StringBuffer();
		// 添加版本号
		String ver = resetBitStrand(byteToBitFS(version), 3);
		sb.append(ver);
		// 添加是否需要ack
		sb.append(needack ? 1 : 0);
		// 添加优先级
		String str_priority = resetBitStrand(byteToBitFS(priority), 3);
		sb.append(str_priority);
		// 添加包序号
		String str_sno = resetBitStrand(Integer.toBinaryString(sno), 9);
		sb.append(str_sno);
		
		//最后封装完成的bit串
		String bitStand = sb.toString();
		
		//将bit串转为byte
		byte b1 = BitToByte(bitStand.substring(0,8));
		byte b2 = BitToByte(bitStand.substring(8,16));
		System.out.println("最后的结果bit串: "+bitStand+"    两个byte:"+b1+"-"+b2);

	}

           到这里就封装就大功告成啦,解析反其道而行就行啦,我就不再多说了。

           这种实现方法不是唯一的,也不是最好的,让各位大神贱笑了,但是也提供了一个例子,揭示了一个道理:代码敲不出的时候就去洗个澡

          



你可能感兴趣的:(笔记)