我有一个带有大括号内部参数的字符串,我将这些参数设置为对象,我想评估它们并将它们替换为我的字符串中的值。
这是我做的,但我不喜欢我做的方式,也许我可以更快地评估我的参数。
(我使用了ls()但是我应该为我的值创建一个data.frame然后我可以轻松地使用它们。)
region <- "france"
name <- "julien"
str <- "/test/{region}/v1.1/{name}/{test}"
df <- data.frame(object = gsub("[\\{\\}]", "", regmatches(str, gregexpr("\\{.*?\\}", str))[[1]]), string = unlist(regmatches(str, gregexpr("\\{.*?\\}", str))), stringsAsFactors = FALSE)
> df
object string
1 region {region}
2 name {name}
3 test {test}
for(i in 1:nrow(df)){
if (df$object[i] %in% ls()){
df$value[i] <- eval(as.name(df$object[i]))
} else {
df$value[i] <- ""
}
str <- gsub(df$string[i], df$value[i], str, fixed = TRUE)
}
> df
object string value
1 region {region} france
2 name {name} julien
3 test {test}
>
> str
[1] "/test/france/v1.1/julien/"
如果有人想要改进代码并使其更有效和更清洁(或直接评估字符串中的参数),感谢您的帮助。
答案 0 :(得分:2)
您可以使用get
或mget
,因为eval
是邪恶的。但是,比在全球环境中飞行所有这些对象更好的策略是创建一个查找表:
df1 <- data.frame(object=c("region", "name"),
value=c("frace", "julien"))
然后你可以使用merge
:
merge(df, df1, all=TRUE)
答案 1 :(得分:1)
get
和exists
可以更轻松地获取价值:
df$value <- sapply(df$object, function(x) if (exists(x)) get(x) else "")
# object string value
# 1 region {region} france
# 2 name {name} julien
# 3 test {test}
另一种方式(没有数据框):
str <- "/test/{region}/v1.1/{name}/{test}"
matches <- regmatches(str,
gregexpr("(?<=\\{)\\w+(?=\\})", str, perl = TRUE))[[1]]
values <- sapply(matches, function(x) if (exists(x)) get(x) else "")
for (i in seq_along(matches)) {
str <- sub(paste0("\\{", matches[i], "\\}"), values[i], str)
}
str
# [1] "/test/france/v1.1/julien/"
答案 2 :(得分:1)
您可以使用gsubfn:
library(gsubfn)
region <- "france"
name <- "julien"
test <- 'toto'
str <- "/test/{region}/v1.1/{name}/{test}"
gsubfn('\\{(\\w+)\\}', get, str)
[1] "/test/france/v1.1/julien/toto"
如果您想从数据框中选择变量:
df <- data.frame(region = 'France', name = 'Julien', test = 'Success',
stringsAsFactors = FALSE)
gsubfn('\\{(\\w+)\\}', function(x) get(x, df), str)
或
gsubfn('\\{(\\w+)\\}', x ~ get(x, df), str)
甚至只是:
gsubfn('\\{(\\w+)\\}', df, str)
它也适用于列表:
L <- list(region = 'France', name = 'Julien', test = 'Success')
gsubfn('\\{(\\w+)\\}', L, str)
答案 3 :(得分:1)
处理字符串时,一个好的经验法则是永远不要使用内置的正则表达式函数,如果你能帮助的话。相反,请使用stringr
包,因为它会使您的代码更清晰。
在这种情况下,您可以通过调用gregexpr
简化regmatches
/ str_match_all
混乱。
括号(
显示要捕获的区域:“至少一个字母字符”,来自[[:alpha:]]+
。这将在第二列中返回
第一列包含完整匹配,其中还包括大括号{
。
library(stringr)
matches <- str_match_all(str, "\\{([[:alpha:]]+)\\}")[[1]]
colnames(matches) <- c("string", "object")
matches
## string object
## [1,] "{region}" "region"
## [2,] "{name}" "name"
## [3,] "{test}" "test"
然后根据Roland的回答,使用查找数据框继续。
lookup <- data.frame(
object = c("region", "name"),
value = c("france", "julien")
)
(df <- merge(matches, lookup, all.x = TRUE))
## object string value
## 1 name {name} julien
## 2 region {region} france
## 3 test {test} <NA>
有关更换值的更新:
由于值需要按顺序更新而不是一次更新,因此for
循环与任何内容一样好。您可以进行一些小的改进。如果1:nrow(df)
可能有零行,df
是个坏主意,因为1:0
不是你想要的。 str_replace_all
比gsub
更容易看到df <- within(
df,
{
string <- as.character(df$string)
value <- ifelse(is.na(value), "", value)
}
)
。
首先,对数据框进行一些更改。字符串列应该是一个字符向量而不是一个因子,并且您需要空字符串而不是缺少值。
str <- "/test/{region}/v1.1/{name}/{test}"
for(i in seq_len(nrow(df)))
{
str <- with(df, str_replace_all(str, fixed(string[i]), value[i]))
}
str
## [1] "/test/france/v1.1/julien/"
更新后的循环如下:
{{1}}