【问题标题】:converting bits in to integer将位转换为整数
【发布时间】:2012-07-10 18:23:20
【问题描述】:

我收到一个包含字节数组的数据包,我必须从中获取一些整数值。 这是文档的一部分。有人可以帮帮我吗?

这是一个 4 字节的数组。

年从 1990 到 2052(6 位),月从 1 到 12(4 位),日从 1 到 31(5 位),小时从 0 到 23(5 位),分钟从 0 到 59(6 位), 秒从 0 到 59(6 位) 默认值:2000 年 1 月 1 日,12:00:00

消息的格式是小端序。

【问题讨论】:

  • 它是否包含一个表格,显示哪些位代表哪些字段?或者是否有任何示例数据显示某个日期对某个值的编码?
  • 布局很重要。例如,数组的第 0 个字节中是 6 位年份吗?这 6 位是字节的最高有效位还是最低有效位?
  • 我猜默认值是0x2842C000。对吗?
  • 如果您将字节数组拆分为每个字段的正确块,您应该能够使用this other question 中的信息来解决您的问题。
  • @Dancrumb:我做到了0x1842C000

标签: java bytearray endianness


【解决方案1】:

您需要的是一些按位运算。首先,用字节构造一个 int:

int n = b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24);

然后,将 int 拆分为组件。现在,您的问题没有指定字段的方向是从右到左还是从左到右。该问题与字节顺序有关,但不完全相同。所以让我们假设字段从左到右。

良好的感觉建议从左到右。这样可以比较时间值的整数表示 - 年位的重要性大于月位等,因此当您比较对应于两个时间点的整数时,您会得到一个按时间顺序正确的结果。

这是给你的心理图像。这是一个整数变量,由位组成:

f e d c b a 9 8 7 6 5 4 3 2 1 0
            -----
              offset is 6, length is 3

让我们定义一个函数,它在给定偏移量(以位为单位)处从 int 获取任意块,并具有给定的长度(以位为单位)。

int bits(int n, int offset, int length)
{
    //shift the bits rightward, so that the desired chunk is at the right end
    n = n >> (31 - offset - length); 

    //prepare a mask where only the rightmost `length`  bits are 1's
    int mask = ~(-1 << length);

    //zero out all bits but the right chunk
    return n & mask;
}

本可以是单行的,但我想让它有点指导意义。通过为每个块手动指定移位因子和掩码,以下答案中的人们有效地内联了此函数。

现在让我们分解。假设 n 来自最顶层的 sn-p:

int year  = bits(n, 0,  6),
    month = bits(n, 6,  4),
    day   = bits(n, 10, 5),
    hour  = bits(n, 15, 5),
    min   = bits(n, 20, 6),
    sec   = bits(n, 26, 6);

我们通过将前面字段的总长度组合在一起来获得偏移量的值。这是在字段从左到右的假设下;如果它们反过来,偏移值就会不同。

这有意义吗?

编辑:如果位块从右到左,那么它是这样的:

int sec   = bits(n, 0,  6),
    min   = bits(n, 6,  6),
    hour  = bits(n, 12, 5),
    day   = bits(n, 17, 5),
    month = bits(n, 22, 4),
    year  = bits(n, 26, 6);

【讨论】:

  • 是的,这是有道理的,我在这里用收到的大量数据进行了尝试,但这些值似乎没有加起来,如果它们的顺序不同,我应该如何处理偏移量?
  • 我不确定算法是否正确(或者我使用错误)。如果n = 421,offset = 2,length = 4,则结果为0。由于我们是在处理java,所以最高位在最左边;所以421 = 0b110100101。当指定偏移量 2 和长度 4 时,我希望得到结果 0b1001。我使用 n &gt;&gt; offset 来获得正确的结果。 (你的面具不错)
  • 它是从左侧偏移,而不是从右侧偏移。偏移量 2 为您提供 25-29 位。
  • 非常感谢!我将使用我的代码,因为我是从右侧抓取的。
  • 对于从右侧抓取,移位线将变为:n = n &lt;&lt; offset; 假设偏移量表示 - 要移出的位数。但总体思路仍然成立。将想要的位移到最右边的位置,屏蔽掉其余的。
【解决方案2】:

首先,我将其转换为整数:

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

int timestamp = ByteBuffer.wrap(byte_array).order(ByteOrder.LITTLE_ENDIAN).getInt();

接下来,我会把它拆开:

int yearCode  = (timestamp >> 26) & 0b111111;
int monthCode = (timestamp >> 22) & 0b1111;
int dayCode   = (timestamp >> 17) & 0b11111;
int hourCode  = (timestamp >> 12) & 0b11111;
int minCode   = (timestamp >> 6)  & 0b111111;
int secCode   = (timestamp >> 0)  & 0b111111;

第一行的遮蔽和最后一行的移位不是绝对必要的,但为了清楚起见,将其保留。

最后一步是将 1900 添加到 yearCode 即可!

【讨论】:

    【解决方案3】:

    假设您有 java 7,您应该能够将年份读取为

        int year = 1990
        + ((b[0] & 0b1000_0000) << 5)
        + ((b[0] & 0b0100_0000) << 4)
        + ((b[0] & 0b0010_0000) << 3)
        + ((b[0] & 0b0001_0000) << 2)
        + ((b[0] & 0b0000_1000) << 1)
        + ((b[0] & 0b0000_0100));
    

    月份为

        int month = 1 
        + ((b[0] & 0b1000_0010) << 3)
        + ((b[0] & 0b0100_0001) << 2)
        + ((b[1] & 0b1000_0000) << 1)
        + ((b[1] & 0b0100_0000));
    

    我让你用同样的方式做其他的 int。

    我没有java7,现在不能测试,希望没有看错。字节的顺序也可能是相反的。

    【讨论】:

    • 您会像那样逐位提取值吗?不是一大块?
    • 是的,这可以优化。但我喜欢用清晰的方式来做,至少在测试证明解码是正确的之前。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-05
    • 1970-01-01
    • 1970-01-01
    • 2012-06-02
    • 2012-04-14
    • 2019-02-10
    相关资源
    最近更新 更多