7

外部程序需要一个带有一些控制参数的输入文件,我希望使用 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,但我无法理解其帮助页面中的任何内容。

我欢迎就更普遍的问题提出其他建议。

4

4 回答 4

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)
于 2013-05-16T19:38:48.820 回答
3

glue提供了另一种选择,

library(glue)
template <- 'Hello {name} You have just won ${value}!'
data <- list( name = "Chris", value= 124)

glue_data(template, .x=data)
# Hello Chris You have just won $124!

glue_data(template, .x=data[-1])
# Error in eval(expr, envir, enclos) : object 'name' not found
于 2017-05-06T22:47:11.800 回答
1

从版本 1.1.0(在 CRAN 2016 年 8 月 19 日)开始,该stringr软件包包含该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
于 2017-09-08T06:56:11.627 回答
1

在准备stringr答案时,我注意到 OP 关于使用的问题brew()到目前为止尚未得到解决。特别是,OP 正在询问如何将他提供data给环境以及如何防止在发生错误时返回字符串。

OP 创建了一个函数own_brew()来包装对brew(). 虽然现在有替代包可用,但我觉得原来的问题值得回答。

这是我改进巴蒂斯特版本的尝试:

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()用于传递 to 的列表,values并且通过测试返回值是否有错误brew()来避免调用。try()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])
Error in cat(name) : object 'name' not found
Error in own_brew(template, data[-1L]) :
于 2017-09-08T07:22:39.827 回答