我一直在阅读Adobe网站上提供的SWF format,它提到为了节省空间,可变位用于存储整数或浮点数(pdf中的第17页)
我一直使用字节对齐的数据,因此没有考虑到位对齐的文件,或者在每个字节中存储信息的情况下进行可变对齐。
例如,您可能有一个包含四个顺序存储的13位整数的结构(而不是将它们存储为四个16位整数)。
前13位是第一个整数,接下来的13位是第二个整数,依此类推。它填充适当的最后一个字节,使结构与文件的其余部分字节对齐,因此52位将填充为56位,需要7个字节来存储这4个整数而不是8个字节。
我认为解决方案归结为在字节数组上使用位操作。
解析四个13位整数的示例解决方案也很好,以演示使用您建议的方法。
答案 0 :(得分:3)
我知道有两种方法可以解决这个问题。第一种是手动执行 - 在字节数组上使用逐位运算符,除法,模数等[或者如果你感到无聊则使用整数/ ulong等]。 IsBitSet Example
另一种方式是BitArray - 它可以为你处理大部分内容:)
最好添加一个BitArray如何处理将13..25位作为int的示例,因为这将是主要操作。乍一看,我只看到一个循环。
很好......我写了一个快速&脏测试概念证明:
var rnd = new Random();
//var data = Enumerable.Range(0, 10).ToArray();
var data = Enumerable.Range(0, 10).Select(x => rnd.Next(1 << 13)).ToArray();
foreach (var n in data) Console.WriteLine(n);
Console.WriteLine(new string('-', 13));
var bits = new BitArray(data.Length * 13);
for (int i = 0; i < data.Length; i++)
{
var intBits = new BitArray(new[] { data[i] });
for (int b = 12; b > -1; b--)
{
bits[i * 13 + b] = intBits[b];
Console.Write(intBits[b] ? 1 : 0);
}
Console.WriteLine();
}
Console.WriteLine(new string('-', 13));
for (int i = 0; i < bits.Length / 13; i++)
{
int number = 0;
for (int b = 12; b > -1; b--)
if (bits[i * 13 + b])
number += 1 << b;
Console.WriteLine(number);
}
Console.ReadLine();
哪个输出:
910
3934
7326
7990
7712
1178
6380
3460
5113
7489
-------------
0001110001110
0111101011110
1110010011110
1111100110110
1111000100000
0010010011010
1100011101100
0110110000100
1001111111001
1110101000001
-------------
910
3934
7326
7990
7712
1178
6380
3460
5113
7489
除了简化访问之外,位阵列没有做太多 - 它仍然非常手动。我希望你能编写自己的类来简化它并使其整洁可重用 - 例如,这是另一个快速概念:
//Improved to take sign into account.
//Sign is in addition to bits allocated for storage in this version.
//Stored as {sign}{bits}
//E.g. -5, stored in 3 bits signed is:
// 1 101
//E.g. 5, stored in 3 bits [with sign turned on]
// 0 101
//E.g. 5, stored in 3 bits no sign
// 101
//This may differ from your exiting format - e.g. you may use two's compliments.
static void Main(string[] args)
{
int bitsPerInt = 13;
//Create your data
var rnd = new Random();
//var data = Enumerable.Range(-5, 10).ToArray();
var data = Enumerable.Range(0, 10).Select(x => rnd.Next(-(1 << bitsPerInt), 1 << bitsPerInt)).ToArray();
var bits = new BitSerlializer();
//Add length header
bits.AddInt(data.Length, 8, false);
foreach (var n in data)
{
bits.AddInt(n, bitsPerInt);
Console.WriteLine(n);
}
//Serialize to bytes for network transfer etc.
var bytes = bits.ToBytes();
Console.WriteLine(new string('-', 10));
foreach (var b in bytes) Console.WriteLine(Convert.ToString(b, 2).PadLeft(8, '0'));
Console.WriteLine(new string('-', 10));
//Deserialize
bits = new BitSerlializer(bytes);
//Get Length Header
var count = bits.ReadInt(8, false);
for (int i = 0; i < count; i++)
Console.WriteLine(bits.ReadInt(bitsPerInt));
Console.ReadLine();
}
public class BitSerlializer
{
List<byte> bytes;
int Position { get; set; }
public BitSerlializer(byte[] initialData = null)
{
if (initialData == null)
bytes = new List<byte>();
else
bytes = new List<byte>(initialData);
}
public byte[] ToBytes() { return bytes.ToArray(); }
public void Addbit(bool val)
{
if (Position % 8 == 0) bytes.Add(0);
if (val) bytes[Position / 8] += (byte)(128 >> (Position % 8));
Position++;
}
public void AddInt(int i, int length, bool isSigned = true)
{
if (isSigned) Addbit(i < 0);
if (i < 0) i = -i;
for (int pos = --length; pos >= 0; pos--)
{
var val = (i & (1 << pos)) != 0;
Addbit(val);
}
}
public bool ReadBit()
{
var val = (bytes[Position / 8] & (128 >> (Position % 8))) != 0;
++Position;
return val;
}
public int ReadInt(int length, bool isSigned = true)
{
var val = 0;
var sign = isSigned && ReadBit() ? -1 : 1;
for (int pos = --length; pos >= 0; pos--)
if (ReadBit())
val += 1 << pos;
return val * sign;
}
}
答案 1 :(得分:3)
另一方面,基于字节数组的方法可能是这样的:
int extend(uint raw, int bits)
{
int sh = 32 - bits;
int x = (int)raw << sh; // puts your sign bit in the highest bit.
return x >> sh; // since x is signed this is an arithmatic signed shift
}
int read(byte[] data, int pos, int bits, bool signed)
{
int fbi = pos / 8; // first byte index
int lbi = (pos + bits - 1) / 8; // last byte index
int cnt = lbi - fbi + 1; // bytes spanned
if (cnt > 3 || lbi >= data.Length) { throw new ArgumentException(); }
uint raw = (uint)(
(data[fbi] << (24 + pos % 8)) +
(cnt < 2 ? 0 : data[fbi + 1] << (16 + pos % 8)) +
(cnt < 3 ? 0 : data[fbi + 2] << (8 + pos % 8))
) >> (32 - bits);
return signed ? extend(raw, bits) : (int)raw;
}
测试:
byte[] test = { 0x55, 0xAA, 0x10 };
string s = "";
s += read(test, 0, 8, false) + "\r\n";
s += read(test, 0, 8, true) + "\r\n";
s += read(test, 8, 8, false) + "\r\n";
s += read(test, 8, 8, true) + "\r\n";
s += read(test, 4, 8, false) + "\r\n";
s += read(test, 7, 9, true) + "\r\n";
s += read(test, 7, 10, true) + "\r\n";
s += read(test, 7, 11, true) + "\r\n";
s += read(test, 7, 12, true) + "\r\n";
s += read(test, 7, 13, true) + "\r\n";
s += read(test, 7, 14, true) + "\r\n";
s += read(test, 7, 15, true) + "\r\n";
s += read(test, 7, 16, true) + "\r\n";
s += read(test, 7, 17, true) + "\r\n";
s += read(test, 18, 2, true) + "\r\n";
s += read(test, 18, 3, true) + "\r\n";
s += read(test, 23, 1, true) + "\r\n";
s += read(test, 23, 2, true) + "\r\n";
测试构建如下字符串:
85
85
170
-86
90
-86
-172
-344
-688
-1375
-2750
-5500
-11000
-22000
1
2
0
然后在最后一行抛出异常。