Подтвердить что ты не робот

С# - сдвиг влево всего массива байтов

В С# существует ли способ сдвига вправо/влево весь массив байтов (и последующее добавление байта к конкретной стороне для последнего бита не теряется)?

Я знаю, что это звучит как странный запрос, но я все равно хотел бы знать, возможно ли его и/или как его начать.

4b9b3361

Ответ 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;
} 

Ответ 2

Просто для усмешки. смещение и поворот байтов в массиве байтов. (не биты)

сдвиг влево, нулевое заполнение:

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

shift right, zero fill:

(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();

Ответ 3

Кажется, вы выполняете бит-операции на большом количестве бит, сохраняя их в байтовом массиве. Рассмотрите возможность использования BitArray class и 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();
    }
}

Ответ 4

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();
    }
}

Ответ 5

Я не думаю, что есть встроенный способ. Я реализовал операцию сдвига влево, описанную ниже (предполагая, что она малочисленна). Это не так элегантно, как вы можете сделать с сборкой x86 (shift with carry instructions), но довольно близко к тому, что вы могли бы сделать с C.

В качестве альтернативы вы можете почти использовать структуру BigInteger (.NET 4 и выше), которая имеет конструктор, который принимает байтовый массив и a 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);
    }

Ответ 6

Я еще раз подумал и понял, что это, вероятно, лучше подходит для вопроса:

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;
}

Аналогичный метод может быть применен для выполнения правого сдвига.