读取字节数组中不同长度位字段的最简单方法是什么?
What is the easiest way to read different length bit fields in a byte array?
我正在尝试读取一个二进制文件(一种图像格式),该文件使用不同大小的位字段来引用不同的颜色,并使用调色板的字典来执行此操作。
例如。
Using the following palette:
0 -> #FFFFFF
10 -> #FF0000
110 -> #FF00DC
111 -> #FF5A0C
The binary data looks like this
0101101110101100010111
问题是,当我读取这个文件时,我得到一个 byte[]
并且我不知道如何用字节处理这些可变长度字段。主要问题是(使用上面的示例)在读取 byte[0]
时我得到 01011011
,这样我就可以将部分数据转换为 #FFFFFF
、#FF0000
, #FF00DC
但我还剩下 00000011
。
所以,问题是,我怎样才能将这个字节的剩余部分与下一个字节连接起来,以便我能够阅读完整的代码。
例如
00000011 + 10101100 = 0000001110101100
并正常阅读。
Obs:我正在使用 c#
编辑:这是我为无损图像压缩而开发的一种格式
这是一个示例位 reader。这不是太有效,因为我返回最低位位置的读取位,然后移位以累积下一个字段。
首先,class 跟踪 byte[]
中的位和字节位置,returns 下一位。
public class BitPosition {
int bytePos = 0;
int bitPos = 0; // 0 - 7 only
Byte[] data;
public BitPosition(Byte[] src) => data = src;
static byte[] byteBitMasks = new byte[] { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
public int ReadNextBit() {
if (bytePos >= data.Length)
throw new IndexOutOfRangeException("ReadNextBit");
int bit = (data[bytePos] & byteBitMasks[bitPos]) == 0 ? 0 : 1;
if (++bitPos > 7) {
bitPos = 0;
++bytePos;
}
return bit;
}
public bool HasMoreData => bytePos < data.Length;
}
现在,class 描述压缩调色板中的每个条目:
public class ColorEntry {
public byte marker;
public int color;
public int sizeInBits;
}
注意:如果您需要更大的标记,可以将byte
替换为int
或uint
。如果您最多需要 64 位标记,则需要修改 ColorReader
以使用 uint64
.
最后,ColorReader
class 使用压缩调色板从 byte[]
读取颜色,BitPosition
class:
public class ColorReader {
BitPosition bp;
public ColorReader(byte[] data) => bp = new BitPosition(data);
static ColorEntry[] palette = new[] {
new ColorEntry { marker = 0b0, color = 0xFFFFFF, sizeInBits = 1 },
new ColorEntry { marker = 0b10, color = 0xFF0000, sizeInBits = 2 },
new ColorEntry { marker = 0b110, color = 0xFF00DC, sizeInBits = 3 },
new ColorEntry { marker = 0b111, color = 0xFF5A0C, sizeInBits = 3 },
};
public IEnumerable<ColorEntry> Colors() {
while (bp.HasMoreData) {
int bitsSoFar = 0;
int numBits = 0;
do {
int nextBit = bp.ReadNextBit();
++numBits;
bitsSoFar |= nextBit;
var nextCE = palette.FirstOrDefault(ce => ce.sizeInBits == numBits && ce.marker == bitsSoFar);
if (nextCE != null) {
yield return nextCE;
break;
}
else
bitsSoFar <<= 1;
} while (true);
}
}
}
您可以像这样使用 class:
var data = new byte[] { 0b01011011, 0b10101100, 0b01011100 };
var cr = new ColorReader(data);
var ans = cr.Colors().Select(c => c.color).ToList();
我正在尝试读取一个二进制文件(一种图像格式),该文件使用不同大小的位字段来引用不同的颜色,并使用调色板的字典来执行此操作。 例如。
Using the following palette:
0 -> #FFFFFF
10 -> #FF0000
110 -> #FF00DC
111 -> #FF5A0C
The binary data looks like this
0101101110101100010111
问题是,当我读取这个文件时,我得到一个 byte[]
并且我不知道如何用字节处理这些可变长度字段。主要问题是(使用上面的示例)在读取 byte[0]
时我得到 01011011
,这样我就可以将部分数据转换为 #FFFFFF
、#FF0000
, #FF00DC
但我还剩下 00000011
。
所以,问题是,我怎样才能将这个字节的剩余部分与下一个字节连接起来,以便我能够阅读完整的代码。 例如
00000011 + 10101100 = 0000001110101100
并正常阅读。
Obs:我正在使用 c#
编辑:这是我为无损图像压缩而开发的一种格式
这是一个示例位 reader。这不是太有效,因为我返回最低位位置的读取位,然后移位以累积下一个字段。
首先,class 跟踪 byte[]
中的位和字节位置,returns 下一位。
public class BitPosition {
int bytePos = 0;
int bitPos = 0; // 0 - 7 only
Byte[] data;
public BitPosition(Byte[] src) => data = src;
static byte[] byteBitMasks = new byte[] { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
public int ReadNextBit() {
if (bytePos >= data.Length)
throw new IndexOutOfRangeException("ReadNextBit");
int bit = (data[bytePos] & byteBitMasks[bitPos]) == 0 ? 0 : 1;
if (++bitPos > 7) {
bitPos = 0;
++bytePos;
}
return bit;
}
public bool HasMoreData => bytePos < data.Length;
}
现在,class 描述压缩调色板中的每个条目:
public class ColorEntry {
public byte marker;
public int color;
public int sizeInBits;
}
注意:如果您需要更大的标记,可以将byte
替换为int
或uint
。如果您最多需要 64 位标记,则需要修改 ColorReader
以使用 uint64
.
最后,ColorReader
class 使用压缩调色板从 byte[]
读取颜色,BitPosition
class:
public class ColorReader {
BitPosition bp;
public ColorReader(byte[] data) => bp = new BitPosition(data);
static ColorEntry[] palette = new[] {
new ColorEntry { marker = 0b0, color = 0xFFFFFF, sizeInBits = 1 },
new ColorEntry { marker = 0b10, color = 0xFF0000, sizeInBits = 2 },
new ColorEntry { marker = 0b110, color = 0xFF00DC, sizeInBits = 3 },
new ColorEntry { marker = 0b111, color = 0xFF5A0C, sizeInBits = 3 },
};
public IEnumerable<ColorEntry> Colors() {
while (bp.HasMoreData) {
int bitsSoFar = 0;
int numBits = 0;
do {
int nextBit = bp.ReadNextBit();
++numBits;
bitsSoFar |= nextBit;
var nextCE = palette.FirstOrDefault(ce => ce.sizeInBits == numBits && ce.marker == bitsSoFar);
if (nextCE != null) {
yield return nextCE;
break;
}
else
bitsSoFar <<= 1;
} while (true);
}
}
}
您可以像这样使用 class:
var data = new byte[] { 0b01011011, 0b10101100, 0b01011100 };
var cr = new ColorReader(data);
var ans = cr.Colors().Select(c => c.color).ToList();