将时间方程转换为 R 日期时间的正则表达式 (POSIXct)

标签 r regex stringr backreference stringi

我正在从另一个平台读取数据,其中使用下面列出的字符串组合来表示时间戳:

\* = current time 
t = current day (00:00)
mo = month 
d = days 
h = hours
m = minutes 

例如,*-3d 是当前时间减去 3 天,t-3h 是今天早上(昨天午夜)之前的三个小时。

我希望能够将这些方程引入 R 并获得相应的 POSIXct 值。我尝试在下面的函数中使用正则表达式,但丢失了每个字符串的数字乘数:

strTimeConverter <- function(z){
  ret <- stringi::stri_replace_all_regex(
    str = z, 
    pattern = c('^\\*', 
                '^t', 
                '([[:digit:]]{1,})mo', 
                '([[:digit:]]{1,})d', 
                '([[:digit:]]{1,})h',
                '([[:digit:]]{1,})m'),
    replacement = c('Sys.time()', 
                    'Sys.Date()', 
                    '*lubridate::months(1)', 
                    '*lubridate::days(1)', 
                    '*lubridate::hours(1)', 
                    '*lubridate::minutes(1)'),
    vectorize_all = F
  )
  return(ret)
  # return(eval(expr = parse(text = ret)))
}

> strTimeConverter('*-5mo+3d+4h+2m')
[1] "Sys.time()-*lubridate::months(1)+*lubridate::days(1)+*lubridate::hours(1)+*lubridate::minutes(1)"

> strTimeConverter('t-5mo+3d+4h+2m')
[1] "Sys.Date()-*lubridate::months(1)+*lubridate::days(1)+*lubridate::hours(1)+*lubridate::minutes(1)"

预期输出:

# *-5mo+3d+4h+2m
"Sys.time()-5*lubridate::months(1)+3*lubridate::days(1)+4*lubridate::hours(1)+4*lubridate::minutes(1)"

# t-5mo+3d+4h+2m
"Sys.Date()-5*lubridate::months(1)+3*lubridate::days(1)+4*lubridate::hours(1)+4*lubridate::minutes(1)"

我认为将 [[:digit]]{1,} 括在括号 () 中会保留它们,但显然这是行不通的。我像这样定义了模式,否则代码会替换重复出现的内容,例如* 转换为 Sys.time() 但随后 Sys.time() 中的 m 被替换为*lubridate::分钟(1)

我计划使用 eval(parse(text = ...)) 将(预期)输出转换为 R 日期时间 - 目前已在函数中注释掉。

我愿意使用其他软件包或方法。

更新

经过一番修改后,我发现以下版本有效 - 我按顺序替换字符串,以便新替换的字符不会再次被替换:

strTimeConverter <- function(z){
  ret <- stringi::stri_replace_all_regex(
    str = z, 
    pattern = c('y', 'd', 'h', 'mo', 'm', '^t', '^\\*'),
    replacement = c('*years(1)',
                    '*days(1)', 
                    '*hours(1)', 
                    '*days(30)',
                    '*minutes(1)',
                    'Sys.Date()', 
                    'Sys.time()'),
    vectorize_all = F
  )
  ret <- gsub(pattern = '\\*', replacement = '*lubridate::', x = ret)
  rdate <- (eval(expr = parse(text = ret)))
  attr(rdate, 'tzone') <- 'UTC'
  return(rdate)
}
sample_string <- '*-5mo+3d+4h+2m'
strTimeConverter(sample_string)

这可以工作,但不是很优雅,并且可能会失败,因为我被迫合并其他表达式(例如 yd 表示一年中的某一天,例如 124)。

最佳答案

您可以在替换中使用反向引用,如下所示:

library(stringr)
x <- c("*-5mo+3d+4h+2m", "t-5mo+3d+4h+2m")
repl <- c('^\\*' = 'Sys.time()', '^t' = 'Sys.Date()', '(\\d+)mo' = '\\1*lubridate::months(1)', '(\\d+)d' = '\\1*lubridate::days(1)',  '(\\d+)h' =  '\\1*lubridate::hours(1)', '(\\d+)m' = '\\1*lubridate::minutes(1)')
stringr::str_replace_all(x, repl)
## => [1] "Sys.time()-5*lubridate::months(1)+3*lubridate::days(1)+4*lubridate::hours(1)+2*lubridate::minutes(1)"
##    [2] "Sys.Date()-5*lubridate::months(1)+3*lubridate::days(1)+4*lubridate::hours(1)+2*lubridate::minutes(1)"

请参阅R demo online .

例如,请参见'(\\d+)mo' = '\\1*lubridate::months(1)'。此处,(\d+)mo 匹配并捕获到组 1 一个或多个数字,并且 mo 只是匹配。然后,当找到匹配项时,\1*lubridate::months(1) 中的 \1 会将第 1 组的内容插入到结果字符串中。

请注意,如果您使用右侧的单词边界 (\b) 限制时间段匹配,可能会使替换更安全:

repl <- c('^\\*' = 'Sys.time()', '^t' = 'Sys.Date()', '(\\d+)mo\\b' = '\\1*lubridate::months(1)', '(\\d+)d\\b' = '\\1*lubridate::days(1)',  '(\\d+)h\\b' =  '\\1*lubridate::hours(1)', '(\\d+)m\\b' = '\\1*lubridate::minutes(1)')

如果时间跨度在没有任何非单词分隔符的情况下相互粘合,则该方法将不起作用,但示例字符串中有 +,因此这里是安全的。

实际上,您也可以使其与您使用的功能一起使用。只需确保反向引用具有 $n 语法:

x <- c("*-5mo+3d+4h+2m", "t-5mo+3d+4h+2m")
pattern = c('^\\*', '^t', '(\\d+)mo', '(\\d+)d', '(\\d+)h', '(\\d+)m')
replacement = c('Sys.time()', 'Sys.Date()', '$1*lubridate::months(1)', '$1*lubridate::days(1)', '$1*lubridate::hours(1)', '$1*lubridate::minutes(1)')
stringi::stri_replace_all_regex(x, pattern, replacement, vectorize_all=FALSE)

输出:

[1] "Sys.time()-5*lubridate::months(1)+3*lubridate::days(1)+4*lubridate::hours(1)+2*lubridate::minutes(1)"
[2] "Sys.Date()-5*lubridate::months(1)+3*lubridate::days(1)+4*lubridate::hours(1)+2*lubridate::minutes(1)"

关于将时间方程转换为 R 日期时间的正则表达式 (POSIXct),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65362829/

相关文章:

regex - 计算 R 中文本字符串中的唯一出现次数

r - 当已知可能的输出时加速 `strsplit`

R:按 ID 聚合并找到最小日期和时间

ruby-on-rails - 如何区分大写和非大写单词?

r - ggplot2 直方图图例太大

css - 使用 vi,如何删除所有包含 [searchterm] 的行?

php - 正则表达式出错

r - 基于变异内匹配正则表达式的条件 str_replace?

R:循环列表列表以检索包含命中的子列表的标题

r - 如何使图形颜色取决于 ggplot2 中的两个标准?