将对象评估为字符串

时间:2014-01-28 08:41:15

标签: r

我有一个带有大括号内部参数的字符串,我将这些参数设置为对象,我想评估它们并将它们替换为我的字符串中的值。

这是我做的,但我不喜欢我做的方式,也许我可以更快地评估我的参数。

(我使用了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/"

如果有人想要改进代码并使其更有效和更清洁(或直接评估字符串中的参数),感谢您的帮助。

4 个答案:

答案 0 :(得分:2)

您可以使用getmget,因为eval是邪恶的。但是,比在全球环境中飞行所有这些对象更好的策略是创建一个查找表:

df1 <- data.frame(object=c("region", "name"),
                  value=c("frace", "julien"))

然后你可以使用merge

merge(df, df1, all=TRUE)

答案 1 :(得分:1)

getexists可以更轻松地获取价值:

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_allgsub更容易看到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}}