如何解决正则表达式将时间方程转换为 R 日期时间 (POSIXct)
我正在从另一个平台读取数据,其中使用下面列出的字符串组合来表示时间戳:
\* = 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',})d',})h',})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()
,但随后 m
中的 Sys.time()
被替换为 *lubridate::minutes(1)
。
我计划使用 eval(parse(text = ...))
将(预期的)输出转换为 R 日期时间 - 目前已在函数中注释掉。
我愿意使用其他软件包或方法。
更新
经过一番修改后,我发现以下版本有效 - 我正在按顺序替换字符串,以便不会再次替换新替换的字符:
strTimeConverter <- function(z){
ret <- stringi::stri_replace_all_regex(
str = z,pattern = c('y','d','h','mo','m','^\\*'),replacement = c('*years(1)','*days(1)','*hours(1)','*days(30)','*minutes(1)','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)"
参见,例如,'(\\d+)mo' = '\\1*lubridate::months(1)'
。在这里,(\d+)mo
匹配并捕获到 Group 1 一个或多个数字,而 mo
只是匹配。然后,当找到匹配项时,\1
中的 \1*lubridate::months(1)
将 Group 1 的内容插入到结果字符串中。
请注意,如果您用右侧的单词边界 (\b
) 限制时间段匹配,可能会使替换更安全:
repl <- c('^\\*' = 'Sys.time()','(\\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)"
,
另一种直接生成时间的选项如下:
strTimeConvert <- function(base=Sys.time(),delta="-5mo+3d+4h+2m"){
mo <- gsub(".*([+-]\\d+)mo.*","\\1",x)
ds <- gsub(".*([+-]\\d+)d.*",x)
hs <- gsub(".*([+-]\\d+)h.*",x)
ms <- gsub(".*([+-]\\d+)m.*",x)
out <- base + months(as.numeric(mo)) + days(as.numeric(ds)) +
hours(as.numeric(hs)) + minutes(as.numeric(ms))
out
}
strTimeConvert()
# [1] "2020-07-21 20:32:19 EDT"
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。