首页 > 解决方案 > C#中的自定义4位数据类型

问题描述

我想创建一个 4 位(半字节)的自定义数据类型。

一种选择是 -

byte source = 0xAD;
var hiNybble = (source & 0xF0) >> 4; //Left hand nybble = A
var loNyblle = (source & 0x0F);      //Right hand nybble = D

但是,我想将每 4 位存储在一个数组中。

例如,一个 2 字节的数组,

00001111 01010000

将作为 4 个半字节存储在自定义数据类型数组中 -

0000
1111
0101
0000

本质上我想对 4 位类型进行操作。

有什么办法可以将字节数组转换为半字节数组?

欣赏一个例子。

谢谢。

标签: c#arraysnibble

解决方案


您可以通过读取然后转换来封装返回 4 位样本的流(从没有编译器的手机编写来测试。预期拼写错误和一个错误):

public static int ReadNibbles(this Stream s, byte[] data, int offset, int count)
{
    if (s == null)
    {
        throw new ArgumentNullException(nameof(s));
    }
    if (data == null)
    {
        throw new ArgumentNullException(nameof(data));
    }
    if (data.Length < offset + length)
    {
        throw new ArgumentOutOfRangeException(nameof(length));
    }

    var readBytes = s.Read(data, offset, length / 2);
    for (int n = readBytes * 2 - 1, k = readBytes - 1; k >= 0; k--)
    {
        data[offset + n--] = data[offset + k] & 0xf;
        data[offset + n--] = data[offset + k] >> 4;
    }
    return readBytes * 2;
}

对 12 位整数执行相同操作(假设 MSB 半字节排序):

public static int Read(this Stream stream, ushort[] data, int offset, int length)
{
    if (stream == null)
    {
        throw new ArgumentNullException(nameof(stream));
    }
    if (data == null)
    {
        throw new ArgumentNullException(nameof(data));
    }
    if (data.Length < offset + length)
    {
        throw new ArgumentOutOfRangeException(nameof(length));
    }
    if (length < 2)
    {
        throw new ArgumentOutOfRangeException(nameof(length), "Cannot read fewer than two samples at a time");
    }
        
    // we always need a multiple of two
    length -= length % 2;

    // 3 bytes     length samples
    // --------- * -------------- = N bytes
    // 2 samples         1
    int rawCount = (length / 2) * 3;

    // This will place GC load.  Something like a buffer pool or keeping
    // the allocation as a field on the reader would be a good idea.
    var rawData = new byte[rawCount];
    int readBytes = 0;
    // if the underlying stream returns an even number of bytes, we will need to try again
    while (readBytes < data.Length)
    {
        int c = stream.Read(rawData, readBytes, rawCount - readBytes);
        if (c <= 0)
        {
            // End of stream
            break;
        }
        readBytes += c;
    }

    // unpack
    int k = 0;
    for (int i = 0; i < readBytes; i += 3)
    {
        // EOF in second byte is undefined
        if (i + 1 >= readBytes)
        {
            throw new InvalidOperationException("Unexpected EOF");
        }

        data[(k++) + offset] = (ushort)((rawData[i + 0] << 4) | (rawData[i + 1] >> 4));

        // EOF in third byte == only one sample
        if (i + 2 < readBytes)
        {
            data[(k++) + offset] = (ushort)(((rawData[i + 1] & 0xf) << 8) | rawData[i + 2]);
        }
    }
    return k;
}

推荐阅读