【问题标题】:c# - left shift an entire byte arrayc# - 左移整个字节数组
【发布时间】:2011-12-09 04:07:53
【问题描述】:

在 C# 中,有没有一种方法可以将整个字节数组右移/左移(并且随后将一个字节添加到特定一侧以保持最后一位不会丢失)?

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

【问题讨论】:

  • 是的:有可能。不:这不是标准的。方法:将移位应用于每个字节,并带有结转。可能需要创建一个新数组,具体取决于扩展语义。
  • @pst,我认为您应该重新创建您的评论作为答案
  • 如果使用 .NET 4,您可以使用BigInteger
  • 您的意思是在单个位的级别上进行移位(例如“将每个字节移位 3 位并带有结转”)还是一次移位一个 整个 字节(例如“在前端/末端添加另一个字节”)?我一开始以为是前者,但现在……
  • 我觉得这是关于 bit 移位的。我正在寻找 byte-shifting。所以我想检查Array.Copy() 是否适合我正在做的事情。随便。

标签: c#


【解决方案1】:

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

左移,零填充:

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

【讨论】:

  • 似乎他在寻求一种方法来移动奇数位,而不是整数字节。
  • “将字节添加到特定的一侧”听起来像字节移位。
  • 如果你左移七位,字节数可能会增加。我很确定这就是问题的意思。而且“所以比特不会丢失”告诉我你绝对不应该打电话给Skip
  • 我把它解释为“带字节扩展的位移”:P~
  • 好的,如果我误读了这个问题,那么让 bitshift 的答案获得投票。对于字节移位,我发现 Linq 解决方案很有趣。 ;>
【解决方案2】:

是的,你可以。看我写的以下方法:

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

【讨论】:

  • 呸。看起来它应该可以工作,但是可以通过将进位或进位到工作寄存器中来清除它,而不是调整每个字节两次。此外,在同一个表达式中移动两个方向过于复杂,它唯一可能影响的是符号位,但你把它扔掉了。 (为什么workRegister 无论如何都要签名,当你不想签名传播时?)
  • 编辑此内容以回应 Ben Voigt 的批评。
  • 实际上并不需要它们,它们是我开始将它们组合在一起的产物。我已经删除了它们。感谢您指出@MartinMulder。
【解决方案3】:

您似乎正在对将它们存储在字节数组中的大量位执行位操作。考虑使用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();
    }
}

【讨论】:

  • 但是这两种类型本身都不包含适当的位移操作。有一个如何在这里使用它们的例子吗?
  • 不确定这应该如何提供帮助,因为BitArray 不提供任何转换运算符或方法。
  • 这取决于您最后要处理的数据。如果您在BitArray 中保留位,则可以将指向起始索引的指针存储在一个整数中。移位将是 - 在末尾添加一个元素并将开始指针移动一。这将在 O(1) 中起作用。如果您有一系列位操作,而不仅仅是这一次移位,则使用 BitArray 可能是个好主意。
  • +1,如果移位频繁而全枚举不频繁,这个解决方案很好。
【解决方案4】:

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

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

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

【讨论】:

    【解决方案5】:

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

    【讨论】:

    • 噗!这是一个字节数组
    【解决方案6】:

    我认为没有内置方法。我实现了您在下面描述的左移操作(假设为小端)。它不像 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);
        }
    

    【讨论】:

      【解决方案7】:

      左移:

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

      【讨论】:

        【解决方案8】:

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

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

        【讨论】:

          猜你喜欢
          • 2023-03-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多