Java的。从不符合字节边界的字节数组中的位中提取整数

时间:2016-11-23 11:44:07

标签: java bitwise-operators

我有以下字节数组: 01010110 01110100 00100101 01001011

这些字节分为两组,用于编码七个整数。我知道第一组由3个值组成,每个值4位(0101 0110 0111),代表数字5,6,7。第二组由4个值组成,每个值为5位(01000 01001 01010 01011),表示整数8,9,10和11。

要提取整数,我目前正在使用以下方法。将数组转换为二进制字符串:

public static String byteArrayToBinaryString(byte[] byteArray)
    {
    String[] arrayOfStrings = new String[byteArray.length];

    for(int i=0; i<byteArray.length; i++)
    {
        arrayOfStrings[i] = byteToBinaryString(byteArray[i]);
    }

    String bitsetString = "";
    for(String testArrayStringElement : arrayOfStrings)
    {
        bitsetString += testArrayStringElement;
    }

    return bitsetString;
}

// Taken from here: http://helpdesk.objects.com.au/java/converting-large-byte-array-to-binary-string
public static String byteToBinaryString(byte byteIn) 
{
    StringBuilder sb = new StringBuilder("00000000");

    for (int bit = 0; bit < 8; bit++) 
    {
        if (((byteIn >> bit) & 1) > 0) 
        {
            sb.setCharAt(7 - bit, '1');
        }
    }

    return sb.toString();
}

然后,我将二进制字符串拆分为2个子字符串:12个字符和20个字符。然后我将每个子串分成新的子串,每个子串的长度等于位数。然后我将每个子子串转换为整数。

它可以工作,但代表数千个整数的字节数组需要30秒到一分钟才能提取。

我在这里有点不知所措。如何使用按位运算符执行此操作?

非常感谢!

2 个答案:

答案 0 :(得分:2)

  

我假设您已经了解了基本bit operations以及如何用Java表达它们。

使用铅笔绘制问题的合成图片

 byte 0     byte 1     byte 2     byte 3
01010110   01110100   00100101   01001011
\__/\__/   \__/\______/\___/\______/\___/
 a   b      c     d      e     f      g

要提取 a b c ,我们需要执行以下操作

   a          b          c

 byte 0     byte 0     byte 1
01010110   01010110   01110100
\.  \.     ||||||||   \.  \.  
  '\  '\   XXXX||||     '\  '\
0.. 0101   0.. 0110   0.. 0111

  Shift       And       Shift

在Java中

int a = byteArray[0] >>> 4, b = byteArray[0] & 0xf, c = byteArray[1] >>> 4;

其他值 d e f g 的计算方法类似,但其中一些需要从数组中读取两个字节(实际上是 d f )。

          d                      e

   byte 1    byte 2            byte 2
  01110100  00100101          00100101
  ||||\\\\  |                 |\\\\\
  XXXX \\\\ |                 X \\\\\
        \\\\|                    \\\\\
  0..   01000                    01001

要计算 d ,我们需要用byteArray[1] & 0xf隔离字节1的至少四位,然后用(byteArray[1] & 0xf) << 1为字节2的位腾出空间,用byteArray[1] >>> 7并最终将结果合并在一起。

int d = (byteArray[1] & 0xf) << 1 | byteArray[2] >>> 7;
int e = (byteArray[2] & 0x7c) >>> 2;
int f = (byteArray[2] & 0x3) << 3 | byteArray[3] >>> 5;
int g = byteArray[3] & 0x1f;

当您熟悉处理位操作时,您可以考虑概括提取整数的函数。

我创建了函数int extract(byte[] bits, int[] sizes, int[] res),它给出了一个字节bits数组,一个大小为sizes的数组,其中偶数索引保存整数的大小以提取比特和奇数索引要提取的整数,输出数组res大到足以容纳输出中的所有整数,从bits中提取sizes表示的所有整数。
它返回提取的整数数。

例如原始问题可以解决为

int res[] = new int[8];
byte bits[] = new byte[]{0x56, 0x74, 0x25, 0x4b};

//Extract 3 integers of 4 bits and 4 integers of 5 bits
int ints = BitsExtractor.extract(bits, new int[]{4, 3,  5, 4}, res);
public class BitsExtractor
{
    public static int extract(byte[] bits, int[] sizes, int[] res)
    {

        int currentByte = 0;            //Index into the bits array
        int intProduced = 0;            //Number of ints produced so far
        int bitsLeftInByte = 8;         //How many bits left in the current byte
        int howManyInts = 0;            //Number of integers to extract 

        //Scan the sizes array two items at a time
        for (int currentSize = 0; currentSize < sizes.length - 1; currentSize += 2)
        {
            //Size, in bits, of the integers to extract
            int intSize = sizes[currentSize];

            howManyInts += sizes[currentSize+1];

            int temp = 0;                   //Temporary value of an integer
            int sizeLeft = intSize;         //How many bits left to extract 


            //Do until we have enough integer or we exhaust the bits array
            while (intProduced < howManyInts && currentByte <= bits.length)
            {
                //How many bit we can extract from the current byte
                int bitSize = Math.min(sizeLeft, bitsLeftInByte);               //sizeLeft <= bitsLeftInByte ? sizeLeft : bitsLeftInByte;
                //The value to mask out the number of bit extracted from
                //The current byte (e.g. for 3 it is 7)
                int byteMask = (1 << bitSize) - 1;
                //Extract the new bits (Note that we extract starting from the
                //RIGHT so we need to consider the bits left in the byte)
                int newBits = (bits[currentByte] >>> (bitsLeftInByte - bitSize)) & byteMask;

                //Create the new temporary value of the current integer by
                //inserting the bits in the lowest positions
                temp = temp << bitSize | newBits;

                //"Remove" the bits processed from the byte
                bitsLeftInByte -= bitSize;

                //Is the byte has been exhausted, move to the next
                if (bitsLeftInByte == 0)
                {
                    bitsLeftInByte = 8;
                    currentByte++;
                }

                //"Remove" the bits processed from the size
                sizeLeft -= bitSize;

                //If we have extracted all the bits, save the integer
                if (sizeLeft == 0)
                {
                    res[intProduced++] = temp;
                    temp = 0;
                    sizeLeft = intSize;
                }
            }
        }

        return intProduced;

    }
}

答案 1 :(得分:1)

我做了第一组,第二组可以用类似的方式完成

public static void main(String args[]) {
        //an example 32 bits like your example
        byte[] bytes = new byte[4];
        bytes[0] = 31;//0001 1111
        bytes[1] = 54;//0011 0110
        bytes[2] = 67;
        bytes[3] = 19;
        //System.out.println(bytes[0]);
        int x = 0;
        int j = -1; // the byte number
        int k = 0; // the bit number in that byte
        int n = 0; // the place of the bit in the integer we are trying to read
        for (int i = 0; i < 32; i++) {

            if (i < 12) { //first group 
                if (i % 8 == 0) {
                    j++;
                    k = 0;

                }
                if (i % 4 == 0) {

                    x = 0;
                    n = 0;
                }

                byte bit = (byte) ((bytes[j] & (1 << (7 - k))) >> (7 - k));
                System.out.println("j is :" + j + " k is :" + k + "  " + bit);
                x = x | bit << (3 - n);
                if ((i + 1) % 4 == 0) {
                    System.out.println(x);
                }
                k++;
                n++;

            } else {

            }

        }

    }

这有点棘手,因为你试图编码一个小于java分配的整数(8位)。所以我不得不采取每一点并“构造”他们的int

获得每一位

byte bit = (byte) ((bytes[j] & (1 << (7 - k))) >> (7 - k));

这将获取我们所在的字节并执行And操作。例如,我想要第一个字节的第3位,我做

bytes[0] &  1 << (7 - 3)

但是这给了我一个超过8位的整数,所以我仍然需要将它移位以获得>> (7 - 3)

的那一位

然后我只用Or x(我们试图解码的int)。用<< (3 - n)将它放在正确的位置。 3因为你的整数是用4位编码的

尝试运行代码并阅读输出。

老实说,我不确定这是否是最佳方式,但我相信它至少比处理字符串更快