C# 整数的变长编码

C# 整数的变长编码,c#,variables,uint,C#,Variables,Uint,在C#中对无符号整数值进行变长编码的最佳方法是什么 “实际目的是将可变长度编码整数(字节)附加到文件头。” 例如:“内容长度”-Http头 这可以通过以下逻辑中的一些更改来实现 我已经写了一些代码,可以做到这一点。如果小值比大值更常见,你可以使用。我使用的一种方法,使较小的值使用更少的字节,就是编码7位数据+1位开销字节 该编码仅适用于从零开始的正值,但也可以在必要时进行修改以处理负值 编码的工作方式如下所示: 获取值的最低7位并将其存储在一个字节中,这就是您要输出的内容 将值向右移动7位

在C#中对无符号整数值进行变长编码的最佳方法是什么


“实际目的是将可变长度编码整数(字节)附加到文件头。”

例如:“内容长度”-Http头

这可以通过以下逻辑中的一些更改来实现



我已经写了一些代码,可以做到这一点。

如果小值比大值更常见,你可以使用。

我使用的一种方法,使较小的值使用更少的字节,就是编码7位数据+1位开销字节

该编码仅适用于从零开始的正值,但也可以在必要时进行修改以处理负值

编码的工作方式如下所示:

  • 获取值的最低7位并将其存储在一个字节中,这就是您要输出的内容
  • 将值向右移动7位,去掉刚才抓取的7位
  • 如果该值为非零(即,从它移开7位后),则在输出前设置要输出的字节的高位
  • 输出字节
  • 如果该值为非零(即导致设置高位的相同检查),则返回并从头开始重复步骤
要解码:

  • 从位0开始
  • 从文件中读取一个字节
  • 存储高位是否已设置,并将其屏蔽
  • 或者在字节的其余部分中输入最终值,即您所在的位位置
  • 如果设置了高位,则将位位置增加7,然后重复步骤,跳过第一步(不要重置位位置)
39 32 31 24 23 16 15 8 7 0 值:| DDDDDDDD | ccccccc | bbbbbb | aaaaaa| 编码:| 0000DDDD | xdddccc | xccccbb | xbbbbbbba | xaaaaaa |(注意,按相反顺序存储) 正如您所看到的,由于控制位的开销,编码值可能会占用一个额外的字节,而这个字节只使用了一半。如果您将其扩展为64位值,那么将完全使用额外的字节,因此仍然只有一个字节的额外开销

注意:由于编码每次存储一个字节的值,因此始终以相同的顺序存储,因此大端或小端系统不会更改此文件的布局。最低有效字节总是先存储,以此类推

范围及其编码大小:

0 - 127 : 1 byte 128 - 16.383 : 2 bytes 16.384 - 2.097.151 : 3 bytes 2.097.152 - 268.435.455 : 4 bytes 268.435.456 - max-int32 : 5 bytes 0-127:1字节 128-16.383:2字节 16.384-2.097.151:3字节 2.097.152-268.435.455:4字节 268.435.456-max-int32:5字节 以下是这两个方面的C#实现:

void Main()
{
    using (FileStream stream = new FileStream(@"c:\temp\test.dat", FileMode.Create))
    using (BinaryWriter writer = new BinaryWriter(stream))
        writer.EncodeInt32(123456789);

    using (FileStream stream = new FileStream(@"c:\temp\test.dat", FileMode.Open))
    using (BinaryReader reader = new BinaryReader(stream))
        reader.DecodeInt32().Dump();
}

// Define other methods and classes here

public static class Extensions
{
    /// <summary>
    /// Encodes the specified <see cref="Int32"/> value with a variable number of
    /// bytes, and writes the encoded bytes to the specified writer.
    /// </summary>
    /// <param name="writer">
    /// The <see cref="BinaryWriter"/> to write the encoded value to.
    /// </param>
    /// <param name="value">
    /// The <see cref="Int32"/> value to encode and write to the <paramref name="writer"/>.
    /// </param>
    /// <exception cref="ArgumentNullException">
    /// <para><paramref name="writer"/> is <c>null</c>.</para>
    /// </exception>
    /// <exception cref="ArgumentOutOfRangeException">
    /// <para><paramref name="value"/> is less than 0.</para>
    /// </exception>
    /// <remarks>
    /// See <see cref="DecodeInt32"/> for how to decode the value back from
    /// a <see cref="BinaryReader"/>.
    /// </remarks>
    public static void EncodeInt32(this BinaryWriter writer, int value)
    {
        if (writer == null)
            throw new ArgumentNullException("writer");
        if (value < 0)
            throw new ArgumentOutOfRangeException("value", value, "value must be 0 or greater");

        do
        {
            byte lower7bits = (byte)(value & 0x7f);
            value >>= 7;
            if (value > 0)
                lower7bits |= 128;
            writer.Write(lower7bits);
        } while (value > 0);
    }

    /// <summary>
    /// Decodes a <see cref="Int32"/> value from a variable number of
    /// bytes, originally encoded with <see cref="EncodeInt32"/> from the specified reader.
    /// </summary>
    /// <param name="reader">
    /// The <see cref="BinaryReader"/> to read the encoded value from.
    /// </param>
    /// <returns>
    /// The decoded <see cref="Int32"/> value.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    /// <para><paramref name="reader"/> is <c>null</c>.</para>
    /// </exception>
    public static int DecodeInt32(this BinaryReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException("reader");

        bool more = true;
        int value = 0;
        int shift = 0;
        while (more)
        {
            byte lower7bits = reader.ReadByte();
            more = (lower7bits & 128) != 0;
            value |= (lower7bits & 0x7f) << shift;
            shift += 7;
        }
        return value;
    }
}
void Main()
{
使用(FileStream stream=newfilestream(@“c:\temp\test.dat”,FileMode.Create))
使用(BinaryWriter=新的BinaryWriter(流))
writer.EncodeInt32(123456789);
使用(FileStream stream=newfilestream(@“c:\temp\test.dat”,FileMode.Open))
使用(BinaryReader=新的BinaryReader(流))
reader.DecodeInt32().Dump();
}
//在此处定义其他方法和类
公共静态类扩展
{
/// 
///使用可变数量的
///字节,并将编码的字节写入指定的写入器。
/// 
/// 
///要将编码值写入的。
/// 
/// 
///要编码和写入的值。
/// 
/// 
///是空的。
/// 
/// 
///小于0。
/// 
/// 
///有关如何从中解码回值的信息,请参见
///a。
/// 
公共静态void EncodeInt32(此二进制编写器,int值)
{
if(writer==null)
抛出新的异常(“编写器”);
如果(值<0)
抛出新ArgumentOutOfRangeException(“value”,value,“value必须为0或更大”);
做
{
字节低7位=(字节)(值&0x7f);
值>>=7;
如果(值>0)
低7位|=128;
writer.Write(低7位);
}而(值>0);
}
/// 
///从可变数量的
///字节,最初使用来自指定读取器的。
/// 
/// 
///要从中读取编码值的。
/// 
/// 
///解码后的值。
/// 
/// 
///是空的。
/// 
公共静态int DecodeInt32(此二进制读取器)
{
如果(读卡器==null)
抛出新的异常(“读取器”);
布尔莫尔=真;
int值=0;
int-shift=0;
而(更多)
{
byte lower7bits=reader.ReadByte();
更多=(低7位和128位)!=0;
值|=(低7位&0x7f)


您应该首先为您的值制作一个直方图。如果分布是随机的(即,直方图中的每一个单元的计数都接近另一个),那么您将无法对该数字进行比二进制表示更有效的编码

如果您的直方图不平衡(即,如果某些值比其他值更多),那么选择对这些值使用较少位的编码可能有意义,而对其他不太可能的值使用更多位

例如,如果需要编码的数字小于15位的可能性是大于15位的2倍,则可以使用第16位来说明这一点,并且只存储/发送16位(如果为零,则下一个字节将形成一个16位数字,可以容纳在32位数字中)。 如果是1,那么接下来的25位将形成一个32位数字。 你在这里失去了一点,但因为它不太可能,最终,对于很多数字,你赢得了更多的位

显然,这是一个微不足道的例子,而将其扩展到两个以上的例子是哈夫曼算法,该算法根据数字出现的概率影响一个接近最优的“码字”

还有一种算术编码算法也能做到这一点(p
void Main()
{
    using (FileStream stream = new FileStream(@"c:\temp\test.dat", FileMode.Create))
    using (BinaryWriter writer = new BinaryWriter(stream))
        writer.EncodeInt32(123456789);

    using (FileStream stream = new FileStream(@"c:\temp\test.dat", FileMode.Open))
    using (BinaryReader reader = new BinaryReader(stream))
        reader.DecodeInt32().Dump();
}

// Define other methods and classes here

public static class Extensions
{
    /// <summary>
    /// Encodes the specified <see cref="Int32"/> value with a variable number of
    /// bytes, and writes the encoded bytes to the specified writer.
    /// </summary>
    /// <param name="writer">
    /// The <see cref="BinaryWriter"/> to write the encoded value to.
    /// </param>
    /// <param name="value">
    /// The <see cref="Int32"/> value to encode and write to the <paramref name="writer"/>.
    /// </param>
    /// <exception cref="ArgumentNullException">
    /// <para><paramref name="writer"/> is <c>null</c>.</para>
    /// </exception>
    /// <exception cref="ArgumentOutOfRangeException">
    /// <para><paramref name="value"/> is less than 0.</para>
    /// </exception>
    /// <remarks>
    /// See <see cref="DecodeInt32"/> for how to decode the value back from
    /// a <see cref="BinaryReader"/>.
    /// </remarks>
    public static void EncodeInt32(this BinaryWriter writer, int value)
    {
        if (writer == null)
            throw new ArgumentNullException("writer");
        if (value < 0)
            throw new ArgumentOutOfRangeException("value", value, "value must be 0 or greater");

        do
        {
            byte lower7bits = (byte)(value & 0x7f);
            value >>= 7;
            if (value > 0)
                lower7bits |= 128;
            writer.Write(lower7bits);
        } while (value > 0);
    }

    /// <summary>
    /// Decodes a <see cref="Int32"/> value from a variable number of
    /// bytes, originally encoded with <see cref="EncodeInt32"/> from the specified reader.
    /// </summary>
    /// <param name="reader">
    /// The <see cref="BinaryReader"/> to read the encoded value from.
    /// </param>
    /// <returns>
    /// The decoded <see cref="Int32"/> value.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    /// <para><paramref name="reader"/> is <c>null</c>.</para>
    /// </exception>
    public static int DecodeInt32(this BinaryReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException("reader");

        bool more = true;
        int value = 0;
        int shift = 0;
        while (more)
        {
            byte lower7bits = reader.ReadByte();
            more = (lower7bits & 128) != 0;
            value |= (lower7bits & 0x7f) << shift;
            shift += 7;
        }
        return value;
    }
}
    public struct VariableLength
    {
        // Variable Length byte array to int
        public VariableLength(byte[] bytes)
        {
            int index = 0;
            int value = 0;
            byte b;
            do
            {
                value = (value << 7) | ((b = bytes[index]) & 0x7F);
                index++;
            } while ((b & 0x80) != 0);

            Length = index;
            Value = value;
            Bytes = new byte[Length];
            Array.Copy(bytes, 0, Bytes, 0, Length);
        }

        // Variable Length int to byte array
        public VariableLength(int value)
        {
            Value = value;
            byte[] bytes = new byte[4];
            int index = 0;
            int buffer = value & 0x7F;

            while ((value >>= 7) > 0)
            {
                buffer <<= 8;
                buffer |= 0x80;
                buffer += (value & 0x7F);
            }
            while (true)
            {
                bytes[index] = (byte)buffer;
                index++;
                if ((buffer & 0x80) > 0)
                    buffer >>= 8;
                else
                    break;
            }

            Length = index;
            Bytes = new byte[index];
            Array.Copy(bytes, 0, Bytes, 0, Length);
        }

        // Number of bytes used to store the variable length value
        public int Length { get; private set; }
        // Variable Length Value
        public int Value { get; private set; }
        // Bytes representing the integer value
        public byte[] Bytes { get; private set; }
    }
public void Example()
{   
//Convert an integer into a variable length byte
int varLenVal = 480;     
VariableLength v = new VariableLength(varLenVal);
byte[] bytes = v.Bytes;

//Convert a variable length byte array into an integer
byte[] varLenByte = new byte[2]{131, 96};     
VariableLength v = new VariableLength(varLenByte);
int result = v.Length;
}
public static int Read7BitEncodedInt(this BinaryReader br) {
    // Read out an Int32 7 bits at a time.  The high bit 
    // of the byte when on means to continue reading more bytes.
    int count = 0;
    int shift = 0;
    byte b;
    do {
        // Check for a corrupted stream.  Read a max of 5 bytes.
        // In a future version, add a DataFormatException.
        if (shift == 5 * 7)  // 5 bytes max per Int32, shift += 7
            throw new FormatException("Format_Bad7BitInt32");

        // ReadByte handles end of stream cases for us. 
        b = br.ReadByte();
        count |= (b & 0x7F) << shift;
        shift += 7;
    } while ((b & 0x80) != 0); 
    return count;
}   

public static void Write7BitEncodedInt(this BinaryWriter br, int value) {
    // Write out an int 7 bits at a time.  The high bit of the byte,
    // when on, tells reader to continue reading more bytes.
    uint v = (uint)value;   // support negative numbers
    while (v >= 0x80) {
        br.Write((byte)(v | 0x80));
        v >>= 7;
    }
    br.Write((byte)v);
}