我有整数数组,我需要将其转换为字节数组
但是我需要(仅且仅仅)取整数数组的每个元素的前11位
然后将其转换为字节数组
我试过这段代码
// ***********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分钟)
我需要一个非常快速的代码(仅限几毫秒)
任何人都可以帮助我吗?
答案 0 :(得分:0)
如果要将int的最低有效11位存储到两个字节中,使得最低有效字节的位为1-8(含),最高有效字节为9-11:
int toStore = 123456789;
byte msb = (byte) ((toStore >> 8) & 7); //or 0b111
byte lsb = (byte) (toStore & 255); //or 0b11111111
要检查这一点,二进制的123456789是:
0b111010110111100110100010101
MMMLLLLLLLL
L以上的位是lsb,其值为21,M以上是msb且值为5
完成工作是移位运算符&gt;&gt;所有二进制数字都滑到右边的8个位置(其中8个从右侧消失 - 它们已经消失,被遗忘):
0b111010110111100110100010101 >> 8 =
0b1110101101111001101
面具操作员&amp; (掩码运算符只保留位,在每个位置,它们在值中为1,在掩码中为1):
0b111010110111100110100010101 &
0b000000000000000000011111111 (255) =
0b000000000000000000000010101
如果您正在处理一个int数组,只需在循环中执行此操作:
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);
}
答案 1 :(得分:0)
基本上,你会做很多转移和掩饰。具体的性质取决于您想要的布局。如果我们假设我们从每个int中包含little-endian,在左边附加 ,那么两个带位置的11位整数:
abcdefghijk lmnopqrstuv
成为8位块:
defghijk rstuvabc 00lmnopq
(即取第一个整数的最低8位,剩下3个,所以将它们打包到下一个字节的低3位,然后取第二个整数的最低5位,最后取剩下的6位)比特,用零填充),然后像这样的东西应该工作:
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位,所以这只是一些模数学运算:
long bits = source.Length * 11;
int len = (int)(bits / 8);
if ((bits % 8) != 0) len++;
byte[] arr = new byte[len];
我们知道输出位置不会与输入匹配,我们知道我们每次都会以不同的位置以字节为单位启动每个11位块,因此为这些位置分配变量,并循环通过输入:
int bitOffset = 0, index = 0;
for (int i = 0; i < source.Length; i++)
{
...
}
return arr;
所以:依次取每个输入(其中输入是位置i
的值),取值的低11位 - 并观察我们有11位(这个值)仍然要写:
int val = source[i] & 2047;
int bitsLeft = 11;
现在,如果当前输出值是部分写入(即bitOffset != 0
),我们应该首先处理。当前输出中剩余的空间量为8 - bitOffset
。由于我们总是有11个输入位,所以我们不需要担心空间比填充值要多,所以:左移我们的值bitOffset
(右侧的焊盘为bitOffset
个零,作为二元运算),&#34;或&#34;输出字节的最低8位。基本上这表示&#34;如果bitOffset
为3,则将val
的5个低位写入输出缓冲区的5个高位&#34 ;;最后,修正这些值:增加我们的写入位置,记录我们仍然要写入的当前值的较少位,并使用右移来丢弃val
的8个低位(由{{1}组成零和bitOffset
&#34;真实&#34;位):
8 - bitOffset
接下来的问题是:我们是否(至少)留下了整个数据字节?例如,如果 if(bitOffset != 0)
{
val = val << bitOffset;
arr[index++] |= (byte)val;
bitsLeft -= (8 - bitOffset);
val >>= 8;
}
为1,我们可能不会这样做(所以我们已经写了7位,只留下4位)。如果我们做,我们可以将其标记为向下并增加写入位置 - 然后再次跟踪剩余的数量并丢弃低8位:
bitOffset
可能我们还有一些遗留问题;例如,如果 if(bitsLeft >= 8)
{
arr[index++] = (byte)val;
bitsLeft -= 8;
val >>= 8;
}
为7,我们将在第一个块中写入1位,在第二个块中写入8位,再写2个 - 或者如果bitOffset
为0则我们赢了&#39 ; t在第一个块中写入任何内容,在第二个块中写入8,剩下3个写入。因此,记下剩余的内容,但不增加写入位置 - 我们已写入低位,但下一个值可能需要写入高位。最后,将bitOffset
更新为,但是我们在最后一步中写了很多低位(可能为零):
bitOffset
if(bitsLeft != 0)
{
arr[index] = (byte)val;
}
bitOffset = bitsLeft;
操作与此逻辑相反 - 再次,计算尺寸并准备状态:
Decode
现在循环输入:
int bits = source.Length * 8;
int len = (int)(bits / 11);
int[] arr = new int[len];
int bitOffset = 0, index = 0;
现在, for(int i = 0; i < source.Length; i++)
{
...
}
return arr;
是我们想要在当前11位值中写入的起始位置,所以如果我们从头开始,它将在第一个时为0字节,然后8;第二个字节的3位与第一个11位整数连接,因此5位成为第二个字节的一部分 - 所以bitOffset
在第3个字节上是5,等等。我们可以计算剩余的位数在当前整数中减去11:
bitOffset
现在我们有3种可能的情况:
1)如果我们更多比当前值剩下的8位,我们可以记下我们的输入(按位&#34;或&#34;)但不增加写入位置(因为我们要为此值写更多内容),并注意我们进一步沿着8位:
int val = source[i] << bitOffset;
int bitsLeftInVal = 11 - bitOffset;
2)如果我们在当前值中剩下完全 8位,我们可以记下我们的输入(按位&#34;或&#34;)和增加写入位置;下一个循环可以从零开始:
if(bitsLeftInVal > 8)
{
arr[index] |= val;
bitOffset += 8;
}
3)否则,我们在当前值中剩下小于 8位;所以我们需要将第一个 else if(bitsLeftInVal == 8)
{
arr[index++] |= val;
bitOffset = 0;
}
位写入当前输出位置(递增输出位置),以及留在 next 输出位置的任何内容。由于我们已经向左移bitsLeftInVal
,这实际上意味着简单:将低11位(bitOffset
)压低到当前位置(按位&#34;或&#34;) ,以及剩下的任何内容(val & 2047
)到下一个 if ,它不会超过我们的输出缓冲区(填充零)。然后计算新的val >> 11
:
bitOffset
基本上就是这样。大量按位操作 - 转换( else
{
arr[index++] |= (val & 2047);
if(index != arr.Length) arr[index] = val >> 11;
bitOffset = 8 - bitsLeftInVal;
}
/ <<
),屏蔽(>>
)和组合(&
)。