R技巧[46]:程序异常或错误处理方法
来源:远方教程 作者:远方教程 发布时间:2019-11-08 查看次数: 6168 访问[新版]
我们希望程序运行过程中,如果碰到一些可以预计的错误,可以自动处理它们,忽略这些异常,继续执行后面的代码,那么可以使用try、tryCatch、withCallingHandlers函数进行异常的处理,让程序继续往下执行。
1. 在R中,有三个函数工具可以解决条件异常处理(包括错误)问题:
- try() 如果出现错误,使用该函数可以跳过错误继续执行程序。
- tryCatch() 指定控制条件,进行异常捕捉,然后采用对应的函数处理异常和错误。
- withCallingHandlers() 是tryCatch()的变体,只是运行的上下文条件不同,它使用的情况很少,但是非常有用。
2. 函数参数详解与示例
-
try()
- R语言中的异常处理和Java类似,使用了try()语句来捕获异常,不过没有对应的catch()语句。
- 在使用try()函数捕获异常后,再对捕获的对象进行解析。
- try()函数第一个参数为调用的方法,第二个参数为是否显示异常消息,如 try(…, silent=TRUE)
如果表达式运行产生错误提示,try()函数会返回一个类(class)对象'try-error'。如果参数 silent=TRUE,错误信息将被隐藏,silent=FALSE,错误信息将显示到屏幕上。在这种情况下,如果'try-error'的错误类型在变量 x.inv 的类型(class)中 ,我们调用 next 语句终止当前循环的执行,进行下一次的循环,否则,我们添加 x.inv 的值到表达式 inverses 。(示例见如下代码)
###求解逆矩阵过程中出错!!!怎么跳过错误!!!### inverses <- vector(mode = "list", 100) x <- matrix(sample(0:2, 4, replace = T), 2, 2) inverses[[count]] <- solve(x) inverses <- vector(mode = "list", 100) x <- matrix(sample(0:2, 4, replace = T), 2, 2) x.inv <- try(solve(x), silent=TRUE) if ('try-error' %in% class(x.inv)) { inverses[[count]] <- x.inv #inverses[!is.null(inverses)] inverses[!(inverses=='NULL')]
try() 允许出现错误后继续执行代码。例如,一般来说,如果你运行的函数引发错误,它会立即终止,并且不返回值:
#> Error in log(x): non-numeric argument to mathematical function
但是,如果将产生错误的语句放在try()中,那么错误信息将被打印,但程序会继续执行:
#> Error in log(x) : non-numeric argument to mathematical function
我们可以使用try(…, silent=TRUE)函数,隐藏错误异常信息。
如果大段代码中有错误,想忽略错误,可以采用try(),但大段代码需放在{ }中:
你可以捕获try()的输出,如果程序运行成功,返回计算结果;如果程序运行不成功,则可以通过class()函数返回,错误类型 'try-error'。
failure <- try("a" + "b") ('try-error' %in% class(success)) ('try-error' %in% class(failure))
在list列表中使用try()函数非常有用,可以有效避免个别元素不能计算引起的错误。
elements <- list(1:10, c(-1, 10), c(T, F), letters) results <- lapply(elements, log) #> Warning in FUN(X[[i]], ...): NaNs produced #> Error in FUN(X[[i]], ...): non-numeric argument to mathematical function results <- lapply(elements, function(x) try(log(x))) #> Warning in log(x): NaNs produced
在R中没有一个可以识别错误类型(class)-"try-error"的函数,我们可以自定义一个函数,然后结合sapply函数,可以非常方便的提取出错误类型、错误的位置以及错误值和正确值。
is.error <- function(x) inherits(x, "try-error") succeeded <- !sapply(results, is.error) # look at successful results #> $ : num [1:10] 0 0.693 1.099 1.386 1.609 ... # look at inputs that failed str(elements[!succeeded]) #> $ : chr [1:26] "a" "b" "c" "d" ...
try()一个非常实用的用法,如下:
try(default <- read.csv("possibly-bad-input.csv"), silent = TRUE)
-
tryCatch()
下面就是tryCatch()函数的标准语法:
}, warning = function(w) {
两个实际的小例子,code:
get.msg <- function(path) con <- file(path, open = "rt", encoding = "latin1") text[seq(which(text == "")[1] + 1, length(text), 1)] return(paste(msg, collapse = "\n")) connect <- dbConnect(MySQL(), dbname="db_olap_web", username="root", password="") }, warning = function(w) {
使用tryCatch()函数,根据获取到的条件信号,返回相应的内置函数处理结果,错误、警告、消息等。
show_condition <- function(code) { error = function(c) "error", warning = function(c) "warning", message = function(c) "message" show_condition(stop("!")) show_condition(warning("?!")) show_condition(message("?"))
我们可以使用tryCatch()函数来实现的try()函数的功能。需要使用conditionMessage()来提取与原来错误相关联的消息。
try2 <- function(code, silent = FALSE) { tryCatch(code, error = function(c) { msg <- conditionMessage(c) invisible(structure(msg, class = "try-error")) try2(stop("Hi"), silent = TRUE)
当返回的错误值信号有缺省值时,但这是我们希望看到更加细节的错误信息,这是就需要我们自己封装一个tryCatch()函数过程,修改错误信息对象,来存储更多的错误信息。下面这个例子是,封装read.csv()函数的错误,将路径名称加到错误信息中!!!
read.csv2 <- function(file, ...) { tryCatch(read.csv(file, ...), error = function(c) { c$message <- paste0(c$message, " (in ", file, ")") read.csv("code/dummy.csv") read.csv2("code/dummy.csv")
在使用tryCatch()捕获异常,中断程序代码时,需要注意可能造成死循环的情况。(除非你 kill R 程序过程!!!)
}, interrupt = function(x) {
tryCatch()还有一个功能模块:finally = { cleanup-code },它指定一个代码块(cleanup-code)(不是函数),无论初始表达是成功还是失败,都运行这段代码块。这对于清理程序(例如,删除文件,关闭连接)非常有用。这个功能等同于使用on.exit(),但它可以被封装在较小的代码块中使用。
-
withCallingHandlers()
与tryCatch()功能相似的另一种方法是withCallingHandlers()。它们的功能之间主要有两个区别:
-
tryCatch()处理程序的返回值由tryCatch()返回,而withCallingHandlers()的返回值被处理程序忽略。
f <- function() stop("!") tryCatch(f(), error = function(e) 1) withCallingHandlers(f(), error = function(e) 1)
-
通过调用sys.calls()查看相应的中间过程,它的运行相当于traceback()的用法,如下所示,它列出了导致当前函数的所有调用。
h <- function() stop("!") tryCatch(f(), error = function(e) print(sys.calls())) # [[1]] tryCatch(f(), error = function(e) print(sys.calls())) # [[2]] tryCatchList(expr, classes, parentenv, handlers) # [[3]] tryCatchOne(expr, names, parentenv, handlers[[1L]]) # [[4]] value[[3L]](cond) withCallingHandlers(f(), error = function(e) print(sys.calls())) # [[1]] withCallingHandlers(f(), # error = function(e) print(sys.calls())) # [[6]] .handleSimpleError( # function (e) print(sys.calls()), "!", quote(h())) # [[7]] h(simpleError(msg, call))
以下为一个示例code:
message2error <- function(code) { withCallingHandlers(code, message = function(e) stop(e)) g <- function() message("Hi!") # Error in message("Hi!"): Hi! # 9: (function (e) stop(e))(list(message = "Hi!\n", # call = message("Hi!"))) # 8: signalCondition(cond) # 7: doWithOneRestart(return(expr), restart) # 6: withOneRestart(expr, restarts[[1L]]) # 4: message("Hi!") at #1 # 2: withCallingHandlers(code, message = function(e) stop(e))
这些细微的差别很少用到,当你试图捕捉究竟哪里出了问题,并把它传递给另一个函数时除外。在大多数情况下,你不应该需要使用withCallingHandlers()。
综合示例
arguments <- commandArgs(trailingOnly=TRUE) myDivide <- function(d, a) { return_value <- 'myDivide warning result' warning("myDivide warning message") } else if (a == 'error') { return_value <- 'myDivide error result' stop("myDivide error message") return_value = d / as.numeric(a) if (a == 'suppress-warnings') { e <- suppressWarnings(myDivide(d,a)) }, warning = function(war) { print(paste("MY_WARNING: ",war)) b <- "changing 'b' inside the warning handler has no effect" }, error = function(err) { print(paste("MY_ERROR: ",err)) b <- "changing 'b' inside the error handler has no effect" print(paste("result =",result))
|
相关文章
图片新闻
|
|
|