6

私はAdobeのサイトで入手可能なSWF形式を読んでいますが、スペースを節約するために、可変ビットを使用して整数または浮動小数点数を格納すると記載されています(PDFの17ページ)

私は常にバイトアラインされたデータを扱ってきたので、ビットアラインされたファイルや、情報が各バイトに格納される可変アラインメントを持っているファイルについてはあまり考えていません。

したがって、たとえば、(4つの16ビット整数として格納するのではなく)4つの13ビット整数を順番に格納する構造体があるとします。

最初の13ビットは最初の整数、次の13ビットは2番目の整数というように続きます。構造体をファイルの残りの部分とバイト整列させるために適切な最後のバイトをパディングするため、52ビットは56ビットにパディングされ、8バイトではなく4つの整数を格納するために7バイトが必要になります。

  • この種の問題にどのようにアプローチしますか?
  • ビットレベルでバイトのストリームを操作するにはどうすればよいですか?
  • このデータを簡単に操作できるようにするために使用できるものはありますか?

解決策は、バイト配列でビット演算を使用することに要約されると思います。

4つの13ビット整数を解析するためのソリューションの例は、提案された方法の使用法を示すためにも役立ちます。

4

2 に答える 2

3

私が知っているこれに対処する方法は2つあります。1 つ目は手動で行うことです。ビット単位の演算子、除算、モジュラスなどをバイト配列で使用します [退屈している場合は integer/ulong など]。 IsBitSet の例

もう 1 つの方法は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;
    }
}
于 2012-08-16T00:53:05.287 に答える
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

次に、最後の行で例外をスローします。

于 2012-08-16T09:43:43.123 に答える