【问题标题】:c# left-shift nibbles in a byte arrayc# 字节数组中的左移半字节
【发布时间】:2023-03-17 03:52:02
【问题描述】:

这是我的功能的要求:

取以下字符串:

6900460420006149231=13050010300100000

并将其转换为以下字节数组:

{ 0x37, 0x06, 0x90, 0x04, 0x60, 0x42, 0x00, 0x06, 0x14, 0x92, 0x31, 0xD1, 0x30, 0x50, 0x01, 0x03, 0x00, 0x10, 0x00, 0x00 }

第一个字节 0x37 是二进制字符串的原始长度。接下来的 10 个字节是以 bcd 格式编码的字符串“6900460420006149231”。这就是棘手的地方。现在我需要十六进制“D”来表示两个字段之间的分隔符(=)。您可以在字节数组中的 12 索引的高半字节中看到十六进制。字节数组的其余部分是以 bcd 格式编码的第二个字段“13050010300100000”。如果原始长度是奇数,我会在未使用数据的前半字节填充前导零。

我不希望任何人都能全面实施,所以让我们分解一下并解决我遇到问题的地方。 假设我有:

byte[] field1Bytes = { 0x06, 0x90, 0x04, 0x60, 0x42, 0x00, 0x06, 0x14, 0x92, 0x31 }
byte[] field2Bytes = { 0x01, 0x30, 0x50, 0x01, 0x03, 0x00, 0x10, 0x00, 0x00 }
byte separator = 13; // D  0x0D

如果我只是使用 Array.Copy,我最终会得到:

{ 0x06, 0x90, 0x04, 0x60, 0x42, 0x00, 0x06, 0x14, 0x92, 0x31, 0x0D, 0x01, 0x30, 0x50, 0x01, 0x03, 0x00, 0x10, 0x00, 0x00 }

上面的字节数组并不是我所需要的。关于如何实现以下函数以使我更接近我想要实现的目标的任何想法:

byte[] ShiftLeftAndCombine(byte[] b1, byte[] b2)

在哪里

ShiftLeftAndCombine({0x0d}, {0x01, 0x30})

会返回

{0xd1, 0x30}
  • 在旁注中,我意识到我需要处理偶数/奇数字段长度来处理我正在编写的实际函数,但让我担心 =]

【问题讨论】:

  • 不,它的工作正常。我得到报酬...
  • 哈哈好吧,不确定,因为这听起来像是一个家庭作业问题! :) 我在下面的回答假设是作业,如果您需要,我会更新它...

标签: c# bytearray bit-manipulation


【解决方案1】:

在我看来,您真正需要做的是将整个第二块内存左移四位。这意味着您要做的不仅仅是复制,您还将把字节 n+1 的“前导”(最高有效)位移动到“尾随”(最低有效)第 n 个字节。这是字节数组的通用“位移器”。

    byte[] shiftBlock(byte[] bytes, short bitShift)
    {
        byte[] newBytes = new byte[bytes.Length+1];
        for (int index=0;index < bytes.Length; index++)
        {
            // Each new byte is the current byte shifted left by "bitShift" bits,
            // followed by the first 8-bitShift bits of the next byte OR zero,
            // if we're at the end of the array. Shift the next-bytes bits to
            // the right, and OR the result together. 

            byte newByteMSB = (byte)(bytes[index] << bitShift); // shift left bitShift bits
            byte newByteLSB = (byte)((index==bytes.Length-1)?((byte)0):(bytes[index+1]));
            newByteLSB = (byte) (newByteLSB >> (8-bitShift));

            newBytes[index] = (byte) ( newByteMSB | newByteLSB);

        }

        return newBytes;
    }

您应该能够将其调整为具有必要注意事项的更广泛的解决方案。我对它进行了粗略的测试,它似乎适用于我扔给它的简单字节数组。希望这会有所帮助!

【讨论】:

  • 谢谢大卫,我刚刚解决了我的功能,但我会为下次添加书签=]
【解决方案2】:

不确定这是否是作业,但左移和组合部分看起来像这样:

var pos = 0;
var newByte = b1[pos] << 4;
newByte |= b2[pos]

显然,您需要在循环中执行此操作并考虑 2 个数组的长度

【讨论】:

  • 左移运算符不需要是 4 而不是 1 吗?
  • 是的 4. 我会给出正确答案,但我现在将使用不同的实现,完成后我将分享。基本上我会将字符串中的每个字符视为一个字节 { 0x06, 0x09, 0x00, 0x00, 0x04, 0x06, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x06, 0x01, 0x04, 0x09, 0x02, 0x03, 00 , 0x0D, 0x01, 0x03, 0x00, 0x05, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 } 然后合并
  • @aelstonjones 好的,想看看你是如何实现的
【解决方案3】:

我会做一个 BCD 类。然后 BCD.ToByteArray() 会以 Byte[] 格式为您提供 BCD 的当前表示,而 BCD.ToString() 将提供字符串格式。在内部将 BCD 存储为每个 BCD 位一个数组元素。如果你设计自己的数据结构而不是试图让 Byte[] 做它不打算做的事情,你会做得更好。

【讨论】:

    【解决方案4】:

    好的,我明白了:

        static void Main(string[] args)
        {
            const string rawTrack = "6900460420006149231=13050010300100000";
    
            var byteList = new LinkedList<byte>();
    
            foreach (var c in rawTrack)
            {
                if(c.Equals('='))
                {
                    byteList.AddLast(13);
                    continue;
                }
    
                var bytes = Formatters.Bcd.GetBytes(new string(c, 1));  // for 9 gives 0x09
                byteList.AddLast(bytes[0]);
            }
    
            // Adjust Buffer if odd length
            if(rawTrack.Length % 2 != 0)
            {
                byteList.AddFirst(0);
            }
    
            var result = new byte[byteList.Count / 2];
            var buffer = new byte[byteList.Count];
            byteList.CopyTo(buffer, 0);
    
            var j = 0;
            for(var i = 0; i < buffer.Length - 1; i += 2, j++ )
            {
                result[j] = CombineLowNibble(buffer[i], buffer[i + 1]);
            }
    
    
            Console.WriteLine(BitConverter.ToString(result));
        }
    
        private static byte CombineLowNibble(byte b, byte b1)
        {
            return (byte) ((b << 4) | b1);
        }
    

    结果如下:

    06-90-04-60-42-00-06-14-92-31-D1-30-50-01-03-00-10-00-00
    

    【讨论】:

    • 干得好!如果您可以将值放入字节数组field1Bytesfield2Bytes,那么您会发现我的答案会执行得更快。但是,如果您对解决方案的性能感到满意,那就去吧!如果您正在处理小长度,那么您会发现List&lt;byte&gt; 将比LinkedList&lt;byte&gt; 快得多。
    • 我需要先添加链表。谢谢你的回答
    • List&lt;byte&gt;.Insert(0, value) 也会让你在开头插入一些东西。对于小列表,这仍然比使用 LinkedList 更快。或者,检查foreach 循环之前的奇数,然后只添加第一个零字节。然后你永远不必在开头插入。
    【解决方案5】:

    编辑:修改为在结果中不包括原始长度。

    希望这会有所帮助。不过,不确定您是如何计算出原始长度为 0x37 的。 :)

    根据所述的预期结果,您只需将分隔符 nybble 移到第二个数组的第一个字节中。

    byte[] field1Bytes = { 0x06, 0x90, 0x04, 0x60, 0x42, 0x00, 0x06, 0x14, 0x92, 0x31 } ;
    byte[] field2Bytes = { 0x01, 0x30, 0x50, 0x01, 0x03, 0x00, 0x10, 0x00, 0x00 } ;
    byte separator = 13; // D  0x0D 
    
    byte[] result = new byte[field1Bytes.Length + field2Bytes.Length];
    
    
    Array.Copy(field1Bytes, 0, result, 0, field1Bytes.Length);
    Array.Copy(field2Bytes, 0, result, field1Bytes.Length, field2Bytes.Length);
    
    // shift the separator into the first byte of the second array in the result
    result[field1Bytes.Length] |= (byte)(separator << 4);
    

    这会产生:

    0x06 0x90 0x04 0x60 0x42 0x00 0x06 0x14 0x92 0x31 0xd1 0x30 0x50 0x01 0x03 0x00 0x10 0x00 0x00
    

    ...与规定的期望结果相匹配。

    【讨论】:

    • 这不会像我认为 OP 的意图那样向左移动 field2Byte 数组 - 只是操作员......假设我正确解释了 OP 的问题:)
    • @DavidW 输出似乎与他想要的输出相匹配。根据他想要的结果,只有第二个数组的第一个字节需要将分隔符值移入。第二个数组的值根本不需要移动。
    • 嗯...明白你在说什么,梦露,但我暂时不同意。如果重点是“打包”两个字节数组,中间有 0xd“夹在中间”,那么在我看来,您肯定必须考虑将第二个数组的内容向左移动四位,否则您将引入四个错误解码信息时为零位。也许错了,谁知道:)
    • @DavidW 只是偏离了他的陈述结果。如果第二个数组的第一个字节 >= 0x80,那么肯定会有一些奇怪的结果。但在这种情况下,您似乎希望将第二个数组 right 移动四位,以保护第一个字节的上 nybble 不丢失。我认为 OP 使用“左移”一词来描述分隔符的处理方式,然后事情就从那里混为一谈了。但是如果没有来自 OP 的更多反馈,这一点是模棱两可的。
    • 同意,梦露,但看起来他的问题已经解决了。但是,移位代码有一天可能会派上用场:) 祝福...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-07
    相关资源
    最近更新 更多