c# - 左移整个字节数组

时间:2011-12-09 04:07:53

标签: c#

在C#中,是否有一种方法可以向右/向左移动整个字节数组(并且随后在最后一位向特定端添加一个字节不会丢失)?

我知道这听起来像是一个奇怪的请求,但我仍然想知道它是否可能和/或如何开始这样做。

8 个答案:

答案 0 :(得分:4)

只是为了笑容。在字节数组中移位和旋转字节。 (不是位移)

左移,零填充:

mybytes.Skip(1).Concat(new byte[] { 0 }).ToArray();

右移,零填充:

(new byte[] {0}).Concat(mybytes.Take(mybytes.Length - 1)).ToArray();

向左旋转:

mybytes.Skip(1).Concat(mybytes.Take(1)).ToArray();

向右旋转:

mybytes.Skip(mbytes.Length - 1).Concat(mbytes.Take(mbytes.Length - 1)).ToArray();

答案 1 :(得分:2)

您似乎正在对存储在字节数组中的大量位执行位操作。考虑使用BitArray类和BitVector32结构。根据您对位的操作,您可以创建这样的类。请注意,移位工作在O(1)而不是O(n)。

public class BitRing : IEnumerable<bool>
{
    private readonly BitArray m_InnerBitArray;

    private int m_StarIndex;

    public BitRing(byte[] bytes)
    {
        m_InnerBitArray = new BitArray(bytes);
        m_StarIndex = 0;
    }

    public void ShiftLeft()
    {
        m_StarIndex++;
    }

    public void ShiftRight()
    {
        m_StarIndex--;
    }

    public bool this[int i]
    {
        get
        {
            int index = GetIndex(i);
            return m_InnerBitArray[index];
        }

        set
        {
            int index = GetIndex(i);
            m_InnerBitArray[index] = value;
        }
    }

    private int GetIndex(int i)
    {
        return i - m_StarIndex%m_InnerBitArray.Count;
    }

    public IEnumerator<bool> GetEnumerator()
    {
        for (int i = m_StarIndex; i < m_InnerBitArray.Count; i++)
        {
            yield return m_InnerBitArray[i];
        }

        for (int i = 0; i < m_StarIndex; i++)
        {
            yield return m_InnerBitArray[i];
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

答案 2 :(得分:1)

是的,你可以。请参阅我写的以下方法:

/// <summary>
/// Rotates the bits in an array of bytes to the left.
/// </summary>
/// <param name="bytes">The byte array to rotate.</param>
public static void RotateLeft(byte[] bytes)
{
    bool carryFlag = ShiftLeft(bytes);

    if (carryFlag == true)
    {
        bytes[bytes.Length - 1] = (byte)(bytes[bytes.Length - 1] | 0x01);
    }
}

/// <summary>
/// Rotates the bits in an array of bytes to the right.
/// </summary>
/// <param name="bytes">The byte array to rotate.</param>
public static void RotateRight(byte[] bytes)
{
    bool carryFlag = ShiftRight(bytes);

    if (carryFlag == true)
    {
        bytes[0] = (byte)(bytes[0] | 0x80);
    }
}

/// <summary>
/// Shifts the bits in an array of bytes to the left.
/// </summary>
/// <param name="bytes">The byte array to shift.</param>
public static bool ShiftLeft(byte[] bytes)
{
    bool leftMostCarryFlag = false;

    // Iterate through the elements of the array from left to right.
    for (int index = 0; index < bytes.Length; index++)
    {
        // If the leftmost bit of the current byte is 1 then we have a carry.
        bool carryFlag = (bytes[index] & 0x80) > 0;

        if (index > 0)
        {
            if (carryFlag == true)
            {
                // Apply the carry to the rightmost bit of the current bytes neighbor to the left.
                bytes[index - 1] = (byte)(bytes[index - 1] | 0x01);
            }
        }
        else
        {
            leftMostCarryFlag = carryFlag;
        }

        bytes[index] = (byte)(bytes[index] << 1);
    }

    return leftMostCarryFlag;
}

/// <summary>
/// Shifts the bits in an array of bytes to the right.
/// </summary>
/// <param name="bytes">The byte array to shift.</param>
public static bool ShiftRight(byte[] bytes) 
{
    bool rightMostCarryFlag = false;
    int rightEnd = bytes.Length - 1;

    // Iterate through the elements of the array right to left.
    for (int index = rightEnd; index >= 0; index--)
    {
        // If the rightmost bit of the current byte is 1 then we have a carry.
        bool carryFlag = (bytes[index] & 0x01) > 0;

        if (index < rightEnd)
        {
            if (carryFlag == true)
            {
                // Apply the carry to the leftmost bit of the current bytes neighbor to the right.
                bytes[index + 1] = (byte)(bytes[index + 1] | 0x80);
            }
        }
        else
        {
            rightMostCarryFlag = carryFlag;
        }

        bytes[index] = (byte)(bytes[index] >> 1);
    }

    return rightMostCarryFlag;
} 

答案 3 :(得分:0)

Linq方式:

static class Shifter
{
    public static byte[] ShiftLeft(this byte[] array, int n)
    {
        var a = array.Select(x => (byte)(x >> 8 - n % 8)).Concat(new byte[(7 + n) / 8]).Select((x, i) => new Tuple<int, byte>(i - (n % 8 == 0 ? 0 : 1), x));
        var b = array.Select(x => (byte)(x << n % 8)).Concat(new byte[n / 8]).Select((x, i) => new Tuple<int, byte>(i, x));

        return (from x in a 
                join y in b on x.Item1 equals y.Item1 into yy
                from y in yy.DefaultIfEmpty()
                select (byte)(x.Item2 | (y == null ? 0 : y.Item2))).ToArray();
    }

    public static byte[] ShiftRight(this byte[] array, int n)
    {
        return (new byte[n/8]).Concat(ShiftLeft(array, (8 - (n%8))%8)).ToArray();
    }
}

答案 4 :(得分:0)

我认为没有内置方式。我实现了你在下面描述的左移操作(假设小端)。它不像使用x86程序集(shift with carry instructions)那样优雅,但与C可以做的非常接近。

或者,您几乎可以使用BigInteger结构(.NET 4及更高版本),它具有一个采用字节数组和 ToByteArray 方法的构造函数。但它的左移操作符号 - 扩展高字节,其右移操作截断。因此,您需要对两者进行补偿,以获得您所描述的确切行为。

    // Left-shifts a byte array in place. Assumes little-endian. Throws on overflow.
    static public void ShiftByteArrayLeft(byte[] array)
    {
        if (array == null)
            throw new ArgumentNullException("array");

        if (array[array.Length - 1] >= 0x80)
            throw new OverflowException();

        // move left-to-right, left-shifting each byte
        for (int i = array.Length - 1; i >= 1; --i)
        {
            // left-shift current byte
            array[i] <<= 1;

            // carry the bit from the next/right byte if needed
            if (array[i - 1] >= 0x80)
                ++array[i];
        }

        // finally shift the left-shift the right-most byte
        array[0] <<= 1;
    }

    // Left-shifts a byte array in place. Assumes little-endian. Grows array as needed.
    static public void ShiftByteArrayLeftAutoGrow(ref byte[] array)
    {
        if (array == null)
            throw new ArgumentNullException("array");

        if (array[array.Length - 1] >= 0x80)
        {
            // allocate a bigger array and do the left-shift on it
            byte[] oldArray = array;
            array = new byte[oldArray.Length + 1];
            Array.Copy(oldArray, 0, array, 0, oldArray.Length);
        }

        ShiftByteArrayLeft(array);
    }

答案 5 :(得分:0)

我已经考虑了一些,并意识到这可能更适合这个问题:

public static void Main()
{
    byte[] bytes = new byte[] { 0xFF, 0x01, 0x80, 0x81 };

    Stack<bool> bitStack = CreateBitStack(bytes);

    ShiftLeftExpand(bitStack, 1);

    byte[] newBytes = CreateByteArray(bitStack);
}

public static void ShiftLeftExpand(Stack<bool> bitStack, int count)
{
    while (count-- > 0)
    {
        bitStack.Push(false);
    }
}

public static Stack<bool> CreateBitStack(byte[] bytes)
{
    Stack<bool> bitStack = new Stack<bool>(bytes.Length * 8);

    for (int bytePosition = 0; bytePosition < bytes.Length; bytePosition++)
    {
        for (int bitPosition = 7; bitPosition >= 0; bitPosition--)
        {
            int bitMask = 0x01 << bitPosition;
            bitStack.Push((bytes[bytePosition] & bitMask) > 0);
        }
    }

    return bitStack;
}

public static byte[] CreateByteArray(Stack<bool> bitStack)
{
    int newArrayLength = (int)Math.Ceiling(bitStack.Count / 8.0);
    byte[] bytes = new byte[newArrayLength];
    int bitCounter = 0;
    while (bitStack.Count > 0)
    {
        bool? bitValue = bitStack.Pop();

        int bitPosition = bitCounter % 8;
        int bytePosition = newArrayLength - 1 - bitCounter / 8;

        if (bitValue == true)
        {
            bytes[bytePosition] = (byte)(bytes[bytePosition] | (0x01 << bitPosition));
        }

        bitCounter++;
    }

    return bytes;
}

可以应用类似的技术来执行右移。

答案 6 :(得分:0)

左移:

for (int i = byteArray.Length - 1; i >= 0; i--) byteArray[i] = (byte)((byteArray[i] << 1) | ((i == 0) ? 0 : byteArray[i - 1] >> 7));

右移:

for (int i = 0; i <= byteArray.Length - 1; i++) byteArray[i] = (byte)((byteArray[i] >> 1) | ((i == byteArray.Length - 1) ? 0 : byteArray[i + 1] << 7));

答案 7 :(得分:0)

这两个函数会将字节数组中的位移位指定的数量,并根据需要将它们移位到相邻字节中。可选地,它们可以将位从数组的一端包装到另一端。请注意,它们创建了一个新数组,但是可以轻松更改代码以改为修改传递的“数组” ...

public static byte[] ShiftRight(byte[] array, int shift, bool wrap = false) {
    if(shift < 0) {
        throw new ArgumentOutOfRangeException("shift");
    } else if(shift == 0) {
        return (byte[])array.Clone();
    } else {
        if(wrap) shift %= (array.Length * 8);
        if(shift >= (array.Length * 8)) return new byte[array.Length];
        var offset = (shift / 8);
        shift %= 8;
        var ʀ = new byte[array.Length];
        for(var ɪ = 0; ɪ < ʀ.Length; ɪ++) {
            var indexL_ɪ = (ɪ + offset);
            var indexH_ɪ = (ɪ + offset + 1);
            if(wrap) {
                if(indexL_ɪ >= array.Length) indexL_ɪ -= array.Length;
                if(indexH_ɪ >= array.Length) indexH_ɪ -= array.Length;
            }
            if(indexL_ɪ < array.Length) ʀ[ɪ] = (byte)(array[indexL_ɪ] >> shift);
            if(indexH_ɪ < array.Length) ʀ[ɪ] |= (byte)(array[indexH_ɪ] << (8 - shift));
        }
        return ʀ;
    }
}

public static byte[] ShiftLeft(byte[] array, int shift, bool wrap = false) {
    if(shift < 0) {
        throw new ArgumentOutOfRangeException("shift");
    } else if(shift == 0) {
        return (byte[])array.Clone();
    } else {
        if(wrap) shift %= (array.Length * 8);
        if(shift >= (array.Length * 8)) return new byte[array.Length];
        var offset = (shift / 8);
        shift %= 8;
        for(var ɪ = 0; ɪ < ʀ.Length; ɪ++) {
            var indexL_ɪ = (ɪ - offset - 1);
            var indexH_ɪ = (ɪ - offset);
            if(wrap) {
                if(indexL_ɪ < 0) indexL_ɪ += array.Length;
                if(indexH_ɪ < 0) indexH_ɪ += array.Length;
            }
            if(indexL_ɪ >= 0) ʀ[ɪ] = (byte)(array[indexL_ɪ] >> (8 - shift));
            if(indexH_ɪ >= 0) ʀ[ɪ] |= (byte)(array[indexH_ɪ] << shift);
        }
        return ʀ;
    }
}