TBCD编码与解码

本文介绍了TBCD编码的基本原理及其转换方法,并提供了一段Java代码实现TBCD编码与字符串之间的相互转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近遇到TBCD解码、编码的问题,经过一番查找。发现其实很简单。原理其实就是把TBCD的编码,高4位和第4位互换,然后每4位转换为十进制。F为空即可。

例如:

TBCD编码:  31 55 40 20 79 F9
解码后原始: 13 55 04 02 97 9               (号码随便敲得,没有任何含义,如有雷同纯属巧合)

提供一个Java的算法实现给大家:

// package tbcd;

/**
  * This sample code demonstrates how a character string can be convered to 
  * a TBCD (Telephony Binary Coded Decimal) string and vice versa.
  */

public class TBCDUtil {
    private static String   cTBCDSymbolString = "0123456789*#abc";
    private static char[]   cTBCDSymbols = cTBCDSymbolString.toCharArray();

    public static void main(String[] args) {

        if (args.length == 0)
            return;
       
        byte[] tbcd = parseTBCD(args[0]);

        System.out.println("TBCD as octets: " + dumpBytes(tbcd));
        System.out.println("TBCD octets decoded: " + toTBCD(tbcd));
    }

    /*
	 * This method converts a TBCD string to a character string.
	 */
    public static java.lang.String toTBCD (byte[] tbcd) {

        int size = (tbcd == null ? 0 : tbcd.length);
        StringBuffer buffer = new StringBuffer(2*size);
        for (int i=0; i<size; ++i) {
            int octet = tbcd[i];
            int n2 = (octet >> 4) & 0xF;
            int n1 = octet & 0xF;
            
            if (n1 == 15) {
                    throw new NumberFormatException("Illegal filler in octet n=" + i);
            }
            buffer.append(cTBCDSymbols[n1]);
            
			if (n2 == 15) {
                if (i != size-1)
                    throw new NumberFormatException("Illegal filler in octet n=" + i);
            } else
                buffer.append(cTBCDSymbols[n2]);
        }

        return buffer.toString();
    }

    /*
	 * This method converts a character string to a TBCD string.
	 */
	public static byte[] parseTBCD (java.lang.String tbcd) {
        int length = (tbcd == null ? 0:tbcd.length());
        int size = (length + 1)/2;
        byte[] buffer = new byte[size];

        for (int i=0, i1=0, i2=1; i<size; ++i, i1+=2, i2+=2) {

            char c = tbcd.charAt(i1);
            int n2 = getTBCDNibble(c, i1);
            int octet = 0;
            int n1 = 15;
            if (i2 < length) {
                c = tbcd.charAt(i2);
                n1 = getTBCDNibble(c, i2);
            }
            octet = (n1 << 4) + n2;
            buffer[i] = (byte)(octet & 0xFF);
        }

        return buffer;
    }

    private static int getTBCDNibble(char c, int i1) {

        int n = Character.digit(c, 10);

        if (n < 0 || n > 9) {
            switch (c) {
                case '*':
                    n = 10;
                    break;
                case '#':
                    n = 11;
                    break;
                case 'a':
                    n = 12;
                    break;
                case 'b':
                    n = 13;
                    break;
                case 'c':
                    n = 14;
					break;
                default:
                    throw new NumberFormatException("Bad character '" + c
                            + "' at position " + i1);
            }
        }
        return n;
    }
  /* Hex chars */
  private static final byte[] HEX_CHAR = new byte[]
      { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };


  /*
   * Helper function that dumps an array of bytes in the hexadecimal format.
   */
  public static final String dumpBytes( byte[] buffer )
  {
      if ( buffer == null )
      {
          return "";
      }

      StringBuffer sb = new StringBuffer();

      for ( int i = 0; i < buffer.length; i++ )
      {
          sb.append( "0x" ).append( ( char ) ( HEX_CHAR[( buffer[i] & 0x00F0 ) >> 4] ) ).append(
              ( char ) ( HEX_CHAR[buffer[i] & 0x000F] ) ).append( " " );
      }

      return sb.toString();
  }
}

<think>我们有一个需求:将BCD编码的字节转换为整数。BCD(Binary-CodedDecimal)是一种用二进制编码表示十进制数字的编码方式,通常每个字节存储两个十进制数字(高4位和低4位分别代表一个十进制数字)。例如:-一个字节的BCD值`0x12`(二进制:00010010)表示十进制数12。-一个字节的BCD值`0x34`表示十进制数34。在用户提供的引用中,我们看到类似的处理(虽然不是直接处理BCD,但使用了位移和掩码操作):```ctemp2=(value2>>4)*10;temp2+=value2&0x0f;```这实际上就是将BCD码转换为整数:高4位(右移4位)乘以10,然后加上低4位(0x0f进行操作得到)。因此,我们可以编写一个函数,将一个字节的BCD码转换为整数。函数原型:```cuint8_tbcd_to_uint8(uint8_tbcd);```但是,用户提到要解析rxdata数组中的角度值。角度值可能是由多个BCD字节组成的。例如,如果角度值由两个字节组成,表示一个0到9999的数,那么我们需要将两个字节的BCD转换为一个整数。假设角度值由两个字节(例如value2和value3)组成,那么我们可以这样处理:-第一个字节(高字节)表示百位和十位(例如0x12表示12*100=1200?),但通常每个字节表示两位十进制数,所以两个字节可以表示4位十进制数(0~9999)。因此,我们可以先转换高字节(乘以100)再加上低字节转换后的值。但是,根据用户之前的代码片段,他们分别处理了value2和value3:```ctemp2=(value2>>4)*10;temp2+=value2&0x0f;temp3=(value3>>4)*10;temp3+=value3&0x0f;```这里temp2和temp3都是0~99之间的数。如果角度值是由这两个字节组合成一个4位数,那么可以:```cangle=temp2*100+temp3;```所以,我们可以写一个函数,将两个字节的BCD转换为一个整数(0~9999)。函数原型:```cuint16_tbcd_to_uint16(uint8_thigh_byte,uint8_tlow_byte);```或者,如果角度值是由多个字节组成,我们可以写一个更通用的函数,比如将n个字节的BCD数组转换为一个整数。但是,用户没有明确角度值由几个字节组成。根据常见的需求,角度值可能由两个字节(0~9999)或三个字节(0~999999)表示。为了灵活性,我们可以先实现一个单字节的转换,然后组合使用。为了简单起见,我们先实现单字节BCD转整数(0~99):```cuint8_tbcd_to_uint8(uint8_tbcd){return(bcd>>4)*10+(bcd&0x0F);}```然后,对于多字节,可以这样组合:```cuint16_ttwo_byte_bcd_to_uint16(uint8_tbcd_high,uint8_tbcd_low){returnbcd_to_uint8(bcd_high)*100+bcd_to_uint8(bcd_low);}```或者,我们可以直接写一个函数处理多个字节:```c//将BCD数组转换为整数,数组从高位到低位排列,每个字节包含两位BCD码//例如:bcd_array[0]是最高位(前两位),bcd_array[1]是接下来的两位,等等。uint32_tbcd_array_to_uint32(constuint8_t*bcd_array,intlen){uint32_tresult=0;for(inti=0;i<len;i++){//对于每个字节,先转换成一个两位整数,然后乘以相应的权重(100的(len-1-i)次方)result=result*100+bcd_to_uint8(bcd_array[i]);}returnresult;}```注意:这里假设每个字节都是BCD编码,并且数组从高位开始(大端序)。现在,用户要解析rxdata数组中的角度值。假设角度值在rxdata数组中的位置是已知的,并且由两个字节组成(例如,索引2和3的位置)。那么我们可以这样:```cuint8_tangle_high=rxdata[2];uint8_tangle_low=rxdata[3];uint16_tangle=two_byte_bcd_to_uint16(angle_high,angle_low);```或者使用通用函数:```cuint8_tangle_bytes[2]={rxdata[2],rxdata[3]};uint16_tangle=bcd_array_to_uint32(angle_bytes,2);```但是,由于角度值可能不会超过9999,所以用uint16_t足够。然而,注意:BCD码中每个半字节只能表示0~9,所以转换时不会出现A~F,如果出现,则不是有效的BCD码。因此,我们可以在转换函数中加入有效性检查。例如:```cuint8_tbcd_to_uint8(uint8_tbcd){uint8_thigh=bcd>>4;uint8_tlow=bcd&0x0F;if(high>9||low>9){//无效BCD码,可以返回0或一个错误值,或者用断言//这里我们返回0,并设置错误标志?或者直接返回0return0;}returnhigh*10+low;}```根据需求,用户可以选择是否进行校验。现在,我们根据用户提供的引用中,他们有一个rxdata数组,里面包含时间字符串,但我们现在要解析的是角度值。所以,我们需要知道角度值在rxdata数组中的具体位置和长度。假设用户告诉我们角度值从索引i开始,长度为2字节。因此,我们提供以下函数:```c#include<stdint.h>//单字节BCD转整数(0~99),如果无效则返回0uint8_tbcd_to_uint8(uint8_tbcd){uint8_thigh=bcd>>4;uint8_tlow=bcd&0x0F;if(high>9||low>9){return0;//或者可以返回一个错误码,或者用其他方式处理错误}returnhigh*10+low;}//两个字节的BCD转整数(0~9999)uint16_tbcd_to_uint16(uint8_tbcd_high,uint8_tbcd_low){uint16_thigh_part=bcd_to_uint8(bcd_high);uint16_tlow_part=bcd_to_uint8(bcd_low);returnhigh_part*100+low_part;}//使用示例:解析rxdata中从位置start_index开始的两个字节的角度值uint16_tparse_angle(constuint8_t*rxdata,intstart_index){returnbcd_to_uint16(rxdata[start_index],rxdata[start_index+1]);}```如果角度值由多个字节组成,我们可以扩展。但根据问题,我们假设是两字节。注意:在用户提供的引用中,他们使用sscanf解析时间字符串。但我们现在处理的是BCD码,所以不需要字符串解析,而是直接对二进制数据进行转换。最后,我们还需要考虑字节序(大端序或小端序)。上面的函数假设先传入高字节,再传入低字节(大端序)。如果数据是小端序(即低字节在前),则需要调整参数顺序。例如,如果数据是小端序(即rxdata[start_index]是低字节,rxdata[start_index+1]是高字节),那么:```cuint16_tparse_angle(constuint8_t*rxdata,intstart_index){//假设数据存储是小端序:低字节在前,高字节在后returnbcd_to_uint16(rxdata[start_index+1],rxdata[start_index]);}```用户需要根据实际数据的存储方式调整。总结步骤:1.实现单字节BCD转整数函数。2.实现多字节组合(这里以两字节为例)。3.根据角度值在rxdata数组中的位置和字节序调用函数。现在,我们编写完整的代码示例:</think>### BCD编码转整数的C语言实现应用 BCD(Binary-Coded Decimal)是一种用4位二进制表示1位十进制的编码方式。在解析角度值等场景中常见。以下是完整的BCD转int函数实现: #### 1. 单字节BCD转整数函数 ```c /** * @brief 将单字节BCD编码转换为整数 * @param bcd 输入的BCD编码字节 * @return 转换后的整数(0-99) */ uint8_t bcd_to_uint8(uint8_t bcd) { // 分离高4位和低4位并转换为十进制 uint8_t high = (bcd >> 4) & 0x0F; // 获取十位数 uint8_t low = bcd & 0x0F; // 获取个位数 // 验证有效性 if(high > 9 || low > 9) { return 0xFF; // 返回错误码 } return high * 10 + low; // 组合为整数 } ``` #### 2. 多字节BCD转整数函数 ```c /** * @brief 将BCD数组转换为整数 * @param bcd_array BCD数组指针 * @param len 数组长度 * @return 转换后的整数 */ uint32_t bcd_array_to_uint32(const uint8_t *bcd_array, int len) { uint32_t result = 0; uint32_t multiplier = 1; // 从最低位开始处理(小端序) for(int i = len - 1; i >= 0; i--) { uint8_t value = bcd_to_uint8(bcd_array[i]); if(value == 0xFF) return 0xFFFFFFFF; // 错误处理 result += value * multiplier; multiplier *= 100; // 每字节代表2位十进制数 } return result; } ``` #### 3. 在角度值解析中的应用 假设`rxdata`数组中包含角度值(如索引2-3位置): ```c // 解析角度值示例 void parse_angle(const uint8_t *rxdata) { // 假设角度值存储在索引2-3(两个字节) uint8_t angle_bytes[2] = {rxdata[2], rxdata[3]}; uint32_t angle = bcd_array_to_uint32(angle_bytes, 2); if(angle != 0xFFFFFFFF) { printf("角度值: %u 度\n", angle); } else { printf("角度值解析错误\n"); } } ``` #### 4. 实现原理说明 1. **BCD编码结构**: - 单字节存储两位十进制数:高4位($b_7-b_4$)为十位,低4位($b_3-b_0$)为个位 - 例如:$0x23 \rightarrow 0010\ 0011 \rightarrow 23$ 2. **转换过程**: ``` value = 0x23 (BCD) high = (0x23 >> 4) = 0x02 → 2 low = (0x23 & 0x0F) = 0x03 → 3 result = 2×10 + 3 = 23 ``` 3. **有效性验证**: - 检查每4位是否$\leq 9$,避免非法BCD值(如0x0A) #### 5. 实际应用场景 在通信协议解析中(如引用[1]的时间解析): ```c // 类似引用[1]的时间解析逻辑 uint8_t time_buf[6]; if(sscanf(rxdata, "...") == 6) { // 将BCD时间字段转换为整数 uint8_t hour = bcd_to_uint8(time_buf[3]); uint8_t min = bcd_to_uint8(time_buf[4]); } ``` ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值