C#字节数组转BCD码和BCD码转整数

5
我有一个由收银机创建的 Hex 文件。我必须读取这个文件。
文件的格式如下所述,类似于套接字数据包。
Code Data: 2 个字节
PLU Code Data: 7 个字节
Unit Price Data: 5 个字节
Quantity Data: 5 个字节
Total Amount Data: 5 个字节
PLU Name Data: 18 个字节
Tax Rate Data: 1 个字节
长度:24 + 19 个字节
  • PLU 码格式为 BCD
  • 单价 1-9999999999(BCD)
  • 数量 1-9999999999(BCD,最后 3 个数字应为小数点)
  • 总金额 1-9999999999(BCD)
我使用二进制读取器读入 Hex 文件,然后将其插入到 Unit Price 字节数组中。
byte[] bytes = { data[21], data[22], data[23], data[24], data[25] }; // BCD Byte Array

这个数组是单价。但是我该如何将这个数字转换为小数呢?信息显示对于数量:BCD的最后一个数字应该是小数--这是什么意思?谢谢。


1
问题非常不清楚,请澄清/详细说明。 - Spikeh
展示一个 byte[] bytes 内容的例子以及你期望的输出。查看 BinaryReader.ReadDecimal() - CodeCaster
1
我试图详细解释它。字节有一个数字,但是我怎么能将其读入整数呢? - xFireTR
你能在以“Code Data: 2 Byte”开头的那一行中加入逗号,以清晰地分隔这些项吗? - hatchet - done with SOverflow
将整数转换为BCD字节数组的方法 - hatchet - done with SOverflow
不确定为什么会有人踩,这个问题对我来说似乎非常清楚——BCD格式在某种程度上是明确定义的——显然BinaryReader.ReadDecimal无法处理BCD格式! - BrainSlugs83
5个回答

10

BCD码将0-9的值编码为4位。在压缩BCD(可能是你正在处理的),一个字节用于包含两个值0-9,分别位于字节的每个半字节(4位)中。要转换为int,需要进行一些位操作。例如,以下代码将把BCD字节数组转换为int,可以容纳最多9个数字。如果输入有超过9个BCD数字,请使用long。

// assume byte[] bcds is input
int result = 0;
foreach(byte bcd in bcds) {
    result *= 100;
    result += (10 * (bcd >> 4));
    result += bcd & 0xf;
}

假设每个字节都以大端BCD格式存储,其中最高位数字位于字节的最高半字节中。这是在BCD的维基百科页面中描述的更常见的实现方式。如果你正在处理小端BCD,则循环内的转换代码将为:

    result *= 100;
    result += (10 * (bcd & 0xf));
    result += bcd >> 4;
你还需要确保数组的字节序是正确的,即数组中的第一个字节包含最重要的两位数字还是最不重要的两位数字。例如,数字123456可以使用压缩BCD占据3个字节。12是在byte [0]还是byte [2]中?如果你的字节序与我的假设不同,你需要调整上面的循环以反转顺序。我假设12在byte [0]中(大端序,左侧字节中有最重要的数字)。
至于所描述的BCD和十进制数量,我需要看实际值才能理解他们在谈论什么。

bcd >>= 4; 不起作用。您需要先复制字节以便修改它。 - Sam Leach
@SamLeach - 感谢你提醒我。我当时是凭记忆写的。根据你的建议,我已经修复了它,并且还发现了另一个错误。现在上面的代码已经经过测试并且可以正常工作了。 - hatchet - done with SOverflow

5
每个字节有两个十进制数字,在每个半字节中各一个。如果您将字节显示为十六进制,则可以轻松读取数字。
0x08 0x27 0x42 0x17 0x75 = 827,421,775

你可以这样获取高位和低位的值:
int high = currentByte >> 4;
int low = currentByte & 0xF;

将每个字节转换成如下的数字:

int number = 10 * high + low;

但要记住,每个字节的大小是下一个字节的100倍。

如果数量有3位小数,请将最终数字除以1,000以获得实际值。


5

正确的代码:

// assume byte[] bcds is input
int result = 0;
foreach(byte bcd in bcds) {
    result *= 100;
    result += (10 * (bcd >> 4));
    result += bcd & 0xf;
}

您可以创建一个公共静态类来为byte[]创建自定义扩展:
public static class BitConverterExtension
{
    public static UInt64 FromBCDToExtUInt64(this byte[] b, byte[] bcds, uint nBytes, uint startOf)
    {
        UInt64 result = 0;
        uint i = 0;

        for (i = 0; i < nBytes; i++)
        {
            result *= 100;
            result += (UInt64)(10 * (bcds[startOf + i] >> 4));
            result += (UInt64)(bcds[startOf + i] & 0xf);
        }

        return (result);

    }
}

2
我写了下面的代码,它对我有效:

我写了这段代码并且对我有效:

 public uint BCD5ToInt(byte [] bcd)
{
uint outInt=0;

   for (int i = 0; i < bcd.Length; i++)
   {
       int mul = (int)Math.Pow(10,(i*2));
       outInt += (uint) (((bcd[i] & 0xF)) * mul);
       mul = (int)Math.Pow(10, (i * 2) + 1);
       outInt +=(uint)( ((bcd[i] >> 4) ) * mul);
   }

   return outInt;
}

这是反向代码:
  // Convert an unsigned integer into 5 bytes of 
    public byte[] IntToBCD5(uint numericvalue, int bytesize = 5)
    {
        byte[] bcd = new byte[bytesize];
        for (int byteNo = 0; byteNo < bytesize; ++byteNo)
            bcd[byteNo] = 0;
        for (int digit = 0; digit < bytesize * 2; ++digit)
        {
            uint hexpart = numericvalue % 10;
            bcd[digit / 2] |= (byte)(hexpart << ((digit % 2) * 4));
            numericvalue /= 10;
        }
        return bcd;
    }

这是测试代码:

 public void test()
    {
        uint firstInt = 987654321;
        var array = IntToBCD5(firstInt);
        var outInt = BCD5ToInt(array);
        MessageBox.Show(outInt.ToString());
    }

0

我的回复可能有点晚,但是这是我解决问题的方法:

1- 首先我需要找到数字的长度,例如:3422 -> 4,100 -> 3

public static class NumbersUtility
{
    public static int FindNumberLength(int number)
    {
        return Convert.ToInt32( Math.Floor(Math.Log(number,10))+1);
    }

    public static int FindNumberDivisor(int number)
    {
        return Convert.ToInt32(Math.Pow(10, FindNumberLength(number)-1));
    }

    public static int[] FindNumberElements(int number)
    {
        int[] elements = new int[FindNumberLength(number)];
        int divisor = FindNumberDivisor(number);
        for (int i = 0; i < elements.Length; i++)
        {
            elements[i] = number/divisor;
            number %= divisor;
            divisor /= 10;
        }
        return elements;
    }
}

接下来,我将数字拆分成一个数组,这样更容易遍历和处理数字。但有一个注意点,如果数字的长度为奇数,则必须在数组开头添加一个零。

       public static byte[] IntToBCD(int[] input, bool isLittleEndian = false)
    {
        byte[] outArr = new byte[Convert.ToInt32(Math.Ceiling((double) input.Length/2))];

        //Handle the case of an odd number in which a zero should be added at the beginning
        if (input.Length%2 != 0)
        {
            //Use a temp array to expand the old one, you can use lists or 
            //anyother datastructure if you wish to
            int[] newInput = new int[input.Length+1];
            Array.Copy(input,0,newInput,1,input.Length);
            newInput[0] = 0;
            input = newInput;
            //Dispose the temp array
            newInput = null;
        }

        for (int i = 0; i < outArr.Length; i++)
        {

            outArr[i]=(byte)(input[i*2]<<4);
            outArr[i]|=(byte)(input[i*2+1]);
        }
        return outArr;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接