2013-05-16 78 views
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

使用正則表達式來檢索模板中的變量名,你可以渲染,如之前驗證,

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) 
+0

謝謝,這是一個選項。我確實喜歡brew有能力評估代碼的事實。 – baptiste

+0

我在我的例子中使用了晶須,但當然這可以很容易地改變,以改爲使用brew模板。 –

+0

很容易...如果有人知道正則表達式:) – baptiste

3

glue package提供了另一種選擇,

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 
1

由於1.1.0版本(在CRAN 19 Aug,2016),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 
1

在編制stringr answer我注意到,任擇議定書的有關的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()通過測試的返回值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]) :