С# - сдвиг влево всего массива байтов
В С# существует ли способ сдвига вправо/влево весь массив байтов (и последующее добавление байта к конкретной стороне для последнего бита не теряется)?
Я знаю, что это звучит как странный запрос, но я все равно хотел бы знать, возможно ли его и/или как его начать.
Ответы
Ответ 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;
}
Аналогичный метод может быть применен для выполнения правого сдвига.