在R语言中解析8字节十六进制整数

4

背景:我正在尝试使用R语言编写解析器,用于解析我首选GPS应用程序导出的跟踪文件。这些文件使用自定义二进制规范,其中纬度、经度和时间戳都表示为8字节的大端有符号整数。例如,纬度是北纬度x10 ^ 7。这是我第一次涉足解析原始/十六进制表示。

假设我有3个原始整数:

# Should parse as 377441228
lat = as.raw(c(0x00, 0x00, 0x00, 0x00, 0x16, 0x7f, 0x4b, 0xcc))
# Should parse as -1195899101
lon = as.raw(c(0xff, 0xff, 0xff, 0xff, 0xb8, 0xb8, 0x07, 0x23))
# Should parse as 1618678057000
time = as.raw(c(0x00, 0x00, 0x01, 0x78, 0xe0, 0xbb, 0x08, 0x28))

我发现的第一种方法是使用readBin()。这对于latlon是正确的,但对于time不起作用。
# 377441228: correct
readBin(lat, integer(), size = 8, 
        signed = TRUE, endian = 'big')
# -1195899101: correct
readBin(lon, integer(), size = 8, 
        signed = TRUE, endian = 'big')
# -524613592: incorrect
readBin(time, integer(), size = 8, 
        signed = TRUE, endian = 'big')

下一步的方法是对字符串进行处理并通过as.numeric()函数转换为数字类型。这种方法对于lattime有效,但对lon无效:
library(magrittr)
parser = function(hex) {
    hex |> 
        paste(collapse = '') %>%
        paste0('0x', .) |> 
        as.numeric()
}
# 377441228: correct
parser(lat)
# 1.844674e+19: incorrect
parser(lon)
# 1.618678e+12: correct
parser(time)

我该如何解析它们?


“time”应该是什么?请记住日期时间的表示方式。(我没有得到那个结果;当使用标准起始值传递给as.POSIXct时,我得到了-524613592,它代表的是“1953年5月17日19:00:08 PDT”)。需要更清晰的问题描述。(我对那些不完整或无法重现的问题感到沮丧,因此提出关闭投票。) - IRTFM
根据第一个块中的注释,time 应该被解析为 1618678057000。它是毫秒而不是秒;我不知道为什么,这只是文件规格。 as.POSIXct(1618678057, origin='1970-01-01') 得到了 2021-04-17 09:47:37 PDT,这是我开始记录用于开发此解析器的轨迹的时间。 - Dan Hicks
时间值太大,无法以R基础整数表示。您可以将其作为double读取并转换为64位整数。library(bit64); \class<-`(readBin(time, double(), size = 8, endian = "big"), "integer64")`. - Ritchie Sacramento
1个回答

1

您可以使用这个小函数,它仅使用基本的R语言。它将原始数据转换为位,将其排序为一个大端向量的1和0,然后使用二进制补码表示法将它们转换为适当的值。

parser <- function(x) {
  bits <- sapply(x, function(y) rev(as.integer(rawToBits(y))))
  sum(bits[-1] * 2^(62:0)) - bits[1] * 2^63
}

测试,我们有:

lat  <- as.raw(c(0x00, 0x00, 0x00, 0x00, 0x16, 0x7f, 0x4b, 0xcc))
lon  <- as.raw(c(0xff, 0xff, 0xff, 0xff, 0xb8, 0xb8, 0x07, 0x23))
time <- as.raw(c(0x00, 0x00, 0x01, 0x78, 0xe0, 0xbb, 0x08, 0x28))

parser(lat)
#> [1] 377441228
parser(lon)
#> [1] -1195898880
parser(time)
#> [1] 1.618678e+12

如果您喜欢一种可以同时处理多个值的向量化版本,您可以这样做:
parser <- function(x) {
  sapply(x, function(z) {
    bits <- sapply(z, function(y) rev(as.integer(rawToBits(y))))
    sum(bits[-1] * 2^(62:0)) - bits[1] * 2^63
  })
}

parser(list(lat, lon, time))
#> [1]     377441228   -1195898880 1618678057000

使用reprex v2.0.2于2023年1月1日创建


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