【问题标题】:Decoding a hex string into 4 pieces that maps to a binary map of values将十六进制字符串解码为 4 段,映射到值的二进制映射
【发布时间】:2016-11-14 03:07:58
【问题描述】:

我正在尝试解码一个所谓的十六进制字符串。在 MS SQL Server (11.0.2100) 中,数据的类型为 char(8)

在手册中没有明确的解码数据的方法,但它记录了它包含的内容:

给定一个十六进制字符串,即。 0001003F,长度为4。低字节 在右边,高字节在左边。对于 4 个中的每一个 'bytes' 一个将“位”映射到某个真值的参考表 被赋予了价值。位顺序也给出了位 0 或位 最右边是第一位,...等

表格如下所示:

第一个“字节”:

|Bit Order  | Description   |   1               |   0               |   trigger     |
|-----------|---------------|-------------------|-------------------|---------------|
|BIT0       | state foo     | state foo is ON   | State foo is OFF  |   high level  |
|BIT1       | state bar     | in state bar      | not in state bar  |   high level  |
|                                   ... 
|BIT7       | state bazz    | in state bazz     | not in state bazz |   high level  |

(对于接下来的 3 个其他“字节”......还有 3 个表格,这 4 个“字节”中的每一个应该有 8 个相等数量的“位”)

我认为解码此数据的方法是将十六进制字符串拆分为 4 部分,并将它们转换为二进制字符串,宽度固定为 8。

PHP 中,以十六进制“0001003F”为例,第一个字节是“3F”,已转换为二进制0011 1111(为清楚起见留有空格)。然后,推断第一个字节的值为:

'state foo is on', 'in state bar', ..., 'not in state bazz'.

我也尝试过:hex2bin("0001003F"),但它输出strin(4) " # "

这是解码这些数据的正确方法吗?

(如果标签不正确,请见谅。)

【问题讨论】:

    标签: php hex hexdump raw-data


    【解决方案1】:

    由于在几乎所有平台(32 位及更高版本)上integer type 的存储空间都适合 4 个字节,因此您可以将十六进制字符串转换为整数,然后使用bitwise operators 检查是否设置了特定位:

    $hex_str = '0001003F';
    $flags = base_convert($hex_str, 16, 10);
    
    foreach (range(0, 31) as $bit) {
      printf("Bit %d: %d\n", $bit, (bool) ($flags & (1 << $bit)));
    }
    

    输出

    Bit 0: 1
    Bit 1: 1
    Bit 2: 1
    Bit 3: 1
    Bit 4: 1
    Bit 5: 1
    Bit 6: 0
    ...
    Bit 15: 0
    Bit 16: 1
    Bit 17: 0
    ...
    Bit 31: 0
    

    如果位$bit 设置为(1),则该位对应的状态为on

    代码借助base_convert 函数将十六进制字符串$hex_str 转换为整数$flags。循环在[0;31] 范围内迭代位数(从最低有效位开始)。 (1 &lt;&lt; $bit) 表达式是将值1 向左移动了$bit 位。因此,如果设置了位数$bit,则按位AND 运算的结果是一个非零整数。如果结果为非零,则将结果转换为boolean 类型以生成1,否则为0

    很容易看出,您可以使用单个按位 AND 操作来测试多个位,例如:

    // Check if at least one of three bits is set, 3rd, 10th, or 11th
    $mask = (1 << 3) | (1 << 10) | (1 << 11);
    if ($flags & $mask)
      printf("At least one of the bits from mask 0x%x is set\n", $mask);
    

    输出

    At least one of the bits from mask 0xc08 is set
    

    【讨论】:

    • 我的解决方案与您的答案非常相似。这种编码/解码在实践中是否存在?这个编解码方案我真的不熟悉。
    • @javiniar.leonard,这取决于问题。特别是,我不认为CHAR(8) 是位掩码的好选择。如果确定位数不会大于 64,那么我宁愿使用BIGINT。如果状态的属性可能会增长,或者这些状态将来可能与其他表有关系,我将为状态创建一个表,并为对象状态创建一个表,例如obj_states (object_id, state_id)。但是,一些 DBMS 实现可能会在 BINARY 字段上进行按位操作(MySQL 8.0 的计划扩展)。
    • 但是以代码复杂性为代价存储十六进制字符显然可以节省一些空间。您可以使用BINARY 字段更好地压缩它们; HEX(bin_field) 可以返回十六进制字符串以便在 PHP 中进行进一步处理。
    • 最后,我能够将我尝试解码的数据与另一个使用相同数据的应用程序进行比较,并且似乎没问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-19
    • 2017-03-09
    • 2021-10-30
    • 2016-12-19
    • 2016-02-26
    • 2021-06-12
    • 2018-01-22
    相关资源
    最近更新 更多