首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将整数数组转换为字节数组的快速方法(11位)

将整数数组转换为字节数组的快速方法(11位)
EN

Stack Overflow用户
提问于 2017-12-15 00:10:24
回答 2查看 393关注 0票数 0

我有整数数组,我需要将它转换为字节数组。

但我需要(仅和仅仅)获取ه整数数组中每个元素的前11位,然后将其转换为字节数组。

我试过这段代码

代码语言:javascript
复制
// ***********convert integer values to byte values
//***********to avoid the left zero padding on the byte array
// *********** first step : convert to binary string 
 // ***********second step : convert binary string to byte array
 // *********** first step
string ByteString = Convert.ToString(IntArray[0], 2).PadLeft(11,'0');
for (int i = 1; i < IntArray.Length; i++)
            ByteString = ByteString + Convert.ToString(IntArray[i], 2).PadLeft(11, '0');



        // ***********second step

        int numOfBytes = ByteString.Length / 8;
        byte[] bytes = new byte[numOfBytes];
        for (int i = 0; i < numOfBytes; ++i)
        {
            bytes[i] = Convert.ToByte(ByteString.Substring(8 * i, 8), 2);

        }

但是它需要太长的时间(如果文件大小很大,代码需要超过1分钟)

我需要一个非常快的代码(只有几毫秒)

有人能帮我吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-12-15 09:57:26

基本上,你会做大量的移动和掩蔽。它的确切性质取决于您想要的布局。如果我们假设我们从每个int中打包小endian,并附加在左侧,那么两个11位整数具有位置:

代码语言:javascript
复制
abcdefghijk lmnopqrstuv

变成8位块:

代码语言:javascript
复制
defghijk  rstuvabc 00lmnopq

(即取第一个整数的最低8位,剩下3位,所以将其打包到下一个字节的低3位,然后取第二个整数的最低5位,最后取其余6位,填充为0),然后这样的东西应该可以工作:

代码语言:javascript
复制
using System;
using System.Linq;

static class Program
{
    static string AsBinary(int val) => Convert.ToString(val, 2).PadLeft(11, '0');
    static string AsBinary(byte val) => Convert.ToString(val, 2).PadLeft(8, '0');
    static void Main()
    {
        int[] source = new int[1432];
        var rand = new Random(123456);
        for (int i = 0; i < source.Length; i++)
            source[i] = rand.Next(0, 2047); // 11 bits

        // Console.WriteLine(string.Join(" ", source.Take(5).Select(AsBinary)));


        var raw = Encode(source);
        // Console.WriteLine(string.Join(" ", raw.Take(6).Select(AsBinary)));
        var clone = Decode(raw);

        // now prove that it worked OK
        if (source.Length != clone.Length)
        {
            Console.WriteLine($"Length: {source.Length} vs {clone.Length}");
        }
        else
        {
            int failCount = 0;
            for (int i = 0; i < source.Length; i++)
            {
                if (source[i] != clone[i] && failCount++ == 0)
                {
                    Console.WriteLine($"{i}: {source[i]} vs {clone[i]}");
                }
            }
            Console.WriteLine($"Errors: {failCount}");
        }
    }
    static byte[] Encode(int[] source)
    {
        long bits = source.Length * 11;
        int len = (int)(bits / 8);
        if ((bits % 8) != 0) len++;

        byte[] arr = new byte[len];
        int bitOffset = 0, index = 0;
        for (int i = 0; i < source.Length; i++)
        {
            // note: this encodes little-endian
            int val = source[i] & 2047;
            int bitsLeft = 11;
            if(bitOffset != 0)
            {
                val = val << bitOffset;
                arr[index++] |= (byte)val;
                bitsLeft -= (8 - bitOffset);
                val >>= 8;
            }

            if(bitsLeft >= 8)
            {
                arr[index++] = (byte)val;
                bitsLeft -= 8;
                val >>= 8;
            }
            if(bitsLeft != 0)
            {
                arr[index] = (byte)val;
            }
            bitOffset = bitsLeft;
        }
        return arr;
    }

    private static int[] Decode(byte[] source)
    {
        int bits = source.Length * 8;
        int len = (int)(bits / 11);
        // note no need to worry about remaining chunks - no ambiguity since 11 > 8

        int[] arr = new int[len];
        int bitOffset = 0, index = 0;
        for(int i = 0; i < source.Length; i++)
        {
            int val = source[i] << bitOffset;
            int bitsLeftInVal = 11 - bitOffset;
            if(bitsLeftInVal > 8)
            {
                arr[index] |= val;
                bitOffset += 8;
            }
            else if(bitsLeftInVal == 8)
            {
                arr[index++] |= val;
                bitOffset = 0;
            }
            else
            {
                arr[index++] |= (val & 2047);
                if(index != arr.Length) arr[index] = val >> 11;
                bitOffset = 8 - bitsLeftInVal;
            }
        }
        return arr;

    }
}

如果您需要一个不同的布局,您将需要调整它。

在我的机器上,它在一秒钟内就编码了512 MiB。

Encode方法概述:

第一件事是预先计算所需的空间数量,并分配输出缓冲区;因为每个输入为输出贡献了11位,这只是一些模数:

代码语言:javascript
复制
    long bits = source.Length * 11;
    int len = (int)(bits / 8);
    if ((bits % 8) != 0) len++;

    byte[] arr = new byte[len];

我们知道输出位置与输入不匹配,而且我们知道每次在不同位置以字节为单位启动每个11位块,因此为这些位置分配变量,并在输入上循环:

代码语言:javascript
复制
    int bitOffset = 0, index = 0;
    for (int i = 0; i < source.Length; i++)
    {
        ...
    }
    return arr;

因此:依次接受每个输入(其中输入是位于i位置的值),取值的低11位-并观察到我们还有11位(此值)仍需写入:

代码语言:javascript
复制
        int val = source[i] & 2047;
        int bitsLeft = 11;

现在,如果当前的输出值是部分写入的(即bitOffset != 0),那么我们应该首先处理这个问题。当前输出中留下的空间为8 - bitOffset。因为我们总是有11个输入位,所以我们不需要担心有比值更多的空间来填充,所以:用bitOffset左移我们的值(右边的bitOffset为零,作为二进制操作),以及输出字节的最小的8位。本质上,这表示“如果bitOffset为3,将val的5位低位写入输出缓冲区的5位高位”;最后,修正值:增加写入位置,记录仍需写入的当前值的位数减少,并使用右移丢弃val的8位低比特(由bitOffset零和8 - bitOffset“实”位组成):

代码语言:javascript
复制
        if(bitOffset != 0)
        {
            val = val << bitOffset;
            arr[index++] |= (byte)val;
            bitsLeft -= (8 - bitOffset);
            val >>= 8;
        }

下一个问题是:我们还剩下(至少)整个字节的数据吗?例如,如果bitOffset是1(所以我们已经写了7位,只剩下4位),我们可能不会这么做。如果我们这样做了,我们就可以将其标记下来并增加写入位置--然后再一次跟踪还剩下多少位,并丢弃低的8位:

代码语言:javascript
复制
        if(bitsLeft >= 8)
        {
            arr[index++] = (byte)val;
            bitsLeft -= 8;
            val >>= 8;
        }

我们可能还有一些遗留下来的东西;例如,如果bitOffset是7,我们会在第一个块中写1位,在第二个块中写8位,剩下2位要写-或者如果bitOffset是0,我们就不会在第一个块中写任何东西,在第二个块中写任何东西,剩下3个要写。所以,记下剩下的内容,但是不增加写入位置--我们已经写入了低位,但是下一个值可能需要写入高位。最后,将bitOffset更新为我们在最后一步中编写的许多低位数(可能为零):

代码语言:javascript
复制
        if(bitsLeft != 0)
        {
            arr[index] = (byte)val;
        }
        bitOffset = bitsLeft;

Decode操作与此逻辑相反--再次计算大小并准备状态:

代码语言:javascript
复制
    int bits = source.Length * 8;
    int len = (int)(bits / 11);

    int[] arr = new int[len];
    int bitOffset = 0, index = 0;

现在循环输入:

代码语言:javascript
复制
    for(int i = 0; i < source.Length; i++)
    {
        ...
    }
    return arr;

现在,bitOffset是我们想要在当前11位值中写入的起始位置,所以如果我们从开始时开始,第一个字节上是0,然后是8;第二个字节的3位与第一个11位整数连接,所以这5位成为第二位的一部分,所以bitOffset在第3字节上是5,等等。我们可以通过从11中减去,来计算当前整数中剩下的位数:

代码语言:javascript
复制
        int val = source[i] << bitOffset;
        int bitsLeftInVal = 11 - bitOffset;

现在我们有三种可能的方案:

1)如果当前值中的大于8位,则可以将输入(按位“或”)标记为“或”,但不要增加写入位置(因为对于此值我们有更多的),请注意,我们在更远的地方是8位:

代码语言:javascript
复制
        if(bitsLeftInVal > 8)
        {
            arr[index] |= val;
            bitOffset += 8;
        }

2)如果在当前值中完全保留了8位,则可以按位“或”对输入进行标记,并增加写入位置;下一个循环可以从零开始:

代码语言:javascript
复制
        else if(bitsLeftInVal == 8)
        {
            arr[index++] |= val;
            bitOffset = 0;
        }

3)否则,在当前值中只剩下不到8位;因此,我们需要将第一个bitsLeftInVal位写入当前输出位置(增加输出位置),以及将剩馀的东西写入下一个输出位置。由于我们已经被bitOffset左移,这实际上意味着:按位“或”将低11位(val & 2047)标记到当前位置,如果不超过输出缓冲区(padding )的话,剩下的任何东西(val >> 11)将被标记到下一个。然后计算我们的新bitOffset

代码语言:javascript
复制
        else
        {
            arr[index++] |= (val & 2047);
            if(index != arr.Length) arr[index] = val >> 11;
            bitOffset = 8 - bitsLeftInVal;
        }

基本上就是这样。很多按位操作--移位(<< / >>)、掩码(&)和组合(|)。

票数 0
EN

Stack Overflow用户

发布于 2017-12-15 00:33:55

如果希望将int中最小的11位存储到两个字节中,那么最小的字节具有1-8位的包含性,而最重要的字节具有9-11位:

代码语言:javascript
复制
int toStore = 123456789;
byte msb = (byte) ((toStore >> 8) & 7); //or 0b111
byte lsb = (byte) (toStore & 255); //or 0b11111111

要检查这一点,二进制数为123456789是:

代码语言:javascript
复制
0b111010110111100110100010101
                  MMMLLLLLLLL

大于L的位是lsb,其值为21,高于m的是msb,值为5。

完成这项工作的是shift操作符>>,其中所有二进制数字都滑到了右边的8个位置(其中8个从右手边消失了--它们消失了,被遗忘了):

代码语言:javascript
复制
0b111010110111100110100010101 >> 8 =
        0b1110101101111001101 

以及掩码操作符&(掩码操作符只保留位,在每个位置上,它们在值上是1,在掩码中也是1):

代码语言:javascript
复制
0b111010110111100110100010101 &
0b000000000000000000011111111 (255) =
0b000000000000000000000010101 

如果要处理int数组,只需在循环中这样做:

代码语言:javascript
复制
byte[] bs = new byte[ intarray.Length*2 ];
for(int x = 0, b=0; x < intarray.Length; x++){
  int toStore = intarray[x];
  bs[b++] = (byte) ((toStore >> 8) & 7);
  bs[b++] = (byte) (toStore & 255);
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47824029

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档