简体   繁体   English

如何从 Stream 写入/读取位? (C#)

[英]How to write/read bits from/to a Stream? (C#)

How can I write bits to a stream (System.IO.Stream) or read in C#?如何将位写入 stream (System.IO.Stream) 或读取 C#? thanks.谢谢。

You could create an extension method on Stream that enumerates the bits, like this: 您可以在Stream上创建一个枚举位的扩展方法,如下所示:

public static class StreamExtensions
{
    public static IEnumerable<bool> ReadBits(this Stream input)
    {
        if (input == null) throw new ArgumentNullException("input");
        if (!input.CanRead) throw new ArgumentException("Cannot read from input", "input");
        return ReadBitsCore(input);
    }

    private static IEnumerable<bool> ReadBitsCore(Stream input)
    {
        int readByte;
        while((readByte = input.ReadByte()) >= 0)
        {
            for(int i = 7; i >= 0; i--)
                yield return ((readByte >> i) & 1) == 1;
        }
    }
}

Using this extension method is easy: 使用此扩展方法很简单:

foreach(bool bit in stream.ReadBits())
{
    // do something with the bit
}

This is not possible with the default stream class. 默认流类无法做到这一点。 The C# (BCL) Stream class operates on the granularity of bytes at it's lowest level. C#(BCL)Stream类在其最低级别的字节粒度上运行。 What you can do is write a wrapper class which reads bytes and partititions them out to bits. 你可以做的是写一个包装类,它读取字节并将它们分配到位。

For example: 例如:

class BitStream : IDisposable {
  private Stream m__stream;
  private byte? m_current;
  private int m_index;
  public byte ReadNextBit() { 
    if ( !m_current.HasValue ) {
      m_current = ReadNextByte();
      m_index = 0;
    }
    var value = (m_byte.Value >> m_index) & 0x1;
    m_index++;
    if (m_index == 8) {
      m_current = null;
    }
    return value;
  }
  private byte ReadNextByte() {
    ...
  }
  // Dispose implementation omitted
}

Note: This will read the bits in right to left fashion which may or may not be what you're intending. 注意:这将读取从右到左方式的位,这可能是也可能不是您想要的。

If you need to retrieve separate sections of your byte stream a few bits at a time, you need to remember the position of the bit to read next between calls.如果您需要一次检索几位字节流的单独部分,则需要记住在调用之间接下来要读取的位的位置。 The accepted answer here does not do that, so each call to the enumerator will start on the next byte of input each time.此处接受的答案并没有这样做,因此每次对枚举器的调用都将从输入的下一个字节开始。 The following extension method for Stream takes care of caching the current byte and the bit position within it between calls.下面的Stream扩展方法负责缓存当前字节和调用之间的位位置。

// Binary MSB-first bit enumeration.
public static class StreamExtensions
{
    static int buffer;
    static int bitPos = -1;

    public static IEnumerable<bool> ReadBits(this Stream input)
    {
        do
        {
            while (bitPos >= 0)
            {
                yield return (buffer & (1 << bitPos--)) > 0;
            }
            // Exhausted all bits, so read the next byte from the stream.
            buffer = input.ReadByte();
            bitPos = 7;  
        } while (buffer > -1);
    }
}

For your purpose, I wrote an easy-to-use, fast and open-source (MIT license) library for this, called "BitStream", which is available at github ( https://github.com/martinweihrauch/BitStream ).为了您的目的,我为此编写了一个易于使用、快速且开源(麻省理工学院许可)的库,称为“BitStream”,可在 github ( https://github.com/martinweihrauch/BitStream ) 获得。

In this example, you can see how 5 unsigned integers, which can be represented with 6 bits (all below the value 63) are written with 6 bits each to a stream and then read back.在此示例中,您可以看到可以用 6 位(均低于值 63)表示的 5 个无符号整数如何分别以 6 位写入 stream,然后再读回。 Please note that the library takes and returns long or ulong values for the ease of it, so just convert your eg int, uint, etc to long/ulong first.请注意,为了方便起见,该库接受并返回 long 或 ulong 值,因此只需先将您的 int、uint 等转换为 long/ulong。

using SharpBitStream;

uint[] testDataUnsigned = { 5, 62, 17, 50, 33 };
var ms = new MemoryStream();
var bs = new BitStream(ms);
Console.WriteLine("Test1: \r\nFirst testing writing and reading small numbers of a max of 6 bits.");

Console.WriteLine("There are 5 unsigned ints , which shall be written into 6 bits each as they are all small than 64: 5, 62, 17, 50, 33");
foreach(var bits in testDataUnsigned)
{
    bs.WriteUnsigned(6, (ulong)bits);
}

Console.WriteLine("The original data are of the size: " + testDataUnsigned.Length + " bytes. The size of the stream is now: " + ms.Length + " bytes\r\nand the bytes in it are: ");

ms.Position = 0;

Console.WriteLine("The resulting bytes in the stream look like this: ");
for (int i = 0; i < ms.Length; i++)
{
    uint bits = (uint)ms.ReadByte();
    Console.WriteLine("Byte #" + Convert.ToString(i).PadLeft(4, '0') + ": " + Convert.ToString(bits, 2).PadLeft(8, '0'));
}

Console.WriteLine("\r\nNow reading the bits back:");
ms.Position = 0;
bs.SetPosition(0, 0);

foreach (var bits in testDataUnsigned)
{
    ulong number = (uint)bs.ReadUnsigned(6);
    Console.WriteLine("Number read: " + number);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM