2012-08-16 30 views
5

我一直在讀SWF format可在Adobe的網站,它提到的是,爲了節省空間,可變比特被用來存儲整數或浮點數(17頁的PDF)讀位對齊數據

我有總是與字節對齊的數據一起工作,所以沒有多少考慮位對齊的文件,或者在每個字節中存儲信息的地方都有可變的對齊方式。因此,例如,您可能有一個包含四個按順序存儲的13位整數(而不是將它們存儲爲四個16位整數)的結構。

第一個13位是第一個整數,接下來的13位是第二個整數,依此類推。它填充適當的最後一個字節以使結構與文件的其餘部分保持字節對齊,所以52位將被填充到56位,需要7個字節來存儲這四個整數,而不是8個字節。

  • 我該如何解決這類問題?
  • 如何在字節級別處理字節流?
  • 有什麼我可以用來幫助更容易使用這些數據?

我想解決方案歸結爲在字節數組上使用位操作。

解析四個13位整數的示例解決方案也很好,可以演示如何使用您的建議方法。

+0

..因爲我不能給你一個完整的答案,或許至少指着你'BitArray'將會有所幫助:) – 2012-08-16 00:41:54

+1

通常這種方法是保持一個uint或ulong位的緩衝區,提取你需要的和當緩衝區中沒有足夠的位時,移入一個新的輸入字節。 – harold 2012-08-16 09:49:53

回答

3

有兩種方法可以處理我所知道的情況。首先是手動做到這一點 - 在字節數組上使用按位運算符,除法,模數等[如果您感到無聊時使用integer/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; 
    } 
} 
+0

這將是一個很好的例子,如何正確BitArray處理得到位13..25作爲一個int,因爲這將是主要的操作。乍一看,我只看到一個循環。 – 2012-08-16 06:01:35

+0

哇!我印象深刻。我相信這並沒有解決這個標誌及其延伸問題,也不確定是否考慮到了性能 - Keikoku沒有提到它,所以可能不會。否則就是一個廣泛答案的絕佳例子。這是+1。 – 2012-08-16 08:30:34

+0

@EugeneRyabtsev - 良好的捕獲,在我的第二個版本的標誌中添加。 – NPSF3000 2012-08-16 10:21:48

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 

然後在最後一行引發異常。

+0

有趣的系統。缺少寫入能力...並且應該被推廣以處理任意長度的位串[因此您可以輕鬆地編寫函數來序列化比int更多的東西]。 – NPSF3000 2012-08-17 04:59:12