外部程序需要带有一些控制参数的输入文件,我希望使用R自动生成。通常,我只需使用paste("parameter1: ", param1, ...)
创建长文本字符串,然后输出到文件,但是脚本迅速变得难以理解。这个问题可能非常适合胡须,
library(whisker)
template= 'Hello {{name}}
You have just won ${{value}}!
'
data <- list( name = "Chris", value= 124)
whisker.render(template, data)
我的问题在于,没有安全检查data
包含所有必需的变量,例如
whisker.render(template, data[-1])
会默默地忽略我忘记指定名字的事实。但是,如果我无法生成完整的配置文件,我的终端程序将崩溃。
另一个模板系统由brew
提供;它具有实际评估事物的优势,并且可能还有助于检测缺失的变量,
library(brew)
template2 = 'Hello <%= name %>
You have just won $<%= value %>!
'
data <- list( name = "Chris", value= 124)
own_brew <- function(template, values){
attach(values, pos=2)
out = capture.output(brew(text = template))
detach(values, pos=2)
cat(out, sep='\n')
invisible(out)
}
own_brew(template2, data)
own_brew(template2, data[-1]) # error
但是,我遇到两个问题:
attach() ... detach()
不理想,(偶尔会发出警告),或者至少我不知道如何正确使用它。我试图为brew()
定义一个环境,但它过于严格,不再了解base
函数了......
即使发生错误,该函数仍会返回一个字符串。我试图将调用包裹在try()
中,但我没有处理错误的经验。如何告诉它退出不产生输出的功能?
编辑:我已更新brew
解决方案以使用新环境而非attach()
,并在出现错误时停止执行。 (?capture.output
表明在这里使用它不是正确的函数,因为“如果在计算表达式时出现错误,则尝试尽可能地将输出写入文件”...)
own_brew <- function(template, values, file=""){
env <- as.environment(values)
parent.env(env) <- .GlobalEnv
a <- textConnection("cout", "w")
out <- try(brew(text = template, envir=env, output=a))
if(inherits(out, "try-error")){
close(a)
stop()
}
cat(cout, file=file, sep="\n")
close(a)
invisible(cout)
}
tryCatch
必须有一种更简单的方法,但我无法理解其帮助页面中的任何内容。
我欢迎有关更普遍问题的其他建议。
答案 0 :(得分:4)
使用正则表达式从模板中检索变量名称,您可以在渲染之前进行验证,例如
render <- function(template, data) {
vars <- unlist(regmatches(template, gregexpr('(?<=\\{\\{)[[:alnum:]_.]+(?=\\}\\})', template, perl=TRUE)))
stopifnot(all(vars %in% names(data)))
whisker.render(template, data)
}
render(template, data)
答案 1 :(得分:1)
自版本1.1.0(2016年8月19日CRAN)以来,stringr
package包含str_interp()
函数(遗憾的是,在发布的NEWS文件中未提及)。
template <- "Hello ${name} You have just won $${value}!"
data <- list( name = "Chris", value= 124)
stringr::str_interp(template, data)
[1] "Hello Chris You have just won $124!"
stringr::str_interp(template, data[-1L])
Error in FUN(X[[i]], ...) : object 'name' not found
答案 2 :(得分:1)
在准备stringr
answer时,我注意到到目前为止OP尚未解决有关brew()
用法的问题。特别是,OP正在询问如何向环境提供data
以及如何防止在发生错误时返回字符串。
OP创建了一个函数own_brew()
,它将调用包装到brew()
。虽然现在有替代套餐,我觉得最初的问题值得回答。
这是我尝试改进baptiste's version:
own_brew <- function(template, values, file=""){
a <- textConnection("cout", "w")
out <- brew::brew(text = template, envir=list2env(values), output=a)
close(a)
if (inherits(out, "try-error")) stop()
cat(cout, file=file, sep="\n")
invisible(cout)
}
主要区别在于list2env()
用于将values
列表传递给brew()
,并且通过测试返回值{{}来避免对try()
的调用1}}表示错误。
out
template <- "Hello <%= name %> You have just won $<%= value %>!" data <- list( name = "Chris", value= 124) own_brew(template, data)
Hello Chris You have just won $124!
own_brew(template, data[-1L])