Cách viết trycatch trong R


342

Tôi muốn viết trycatchmã để xử lý lỗi khi tải xuống từ web.

url <- c(
    "http://stat.ethz.ch/R-manual/R-devel/library/base/html/connections.html",
    "http://en.wikipedia.org/wiki/Xz")
y <- mapply(readLines, con=url)

Hai câu lệnh này chạy thành công. Dưới đây, tôi tạo một địa chỉ web không tồn tại:

url <- c("xxxxx", "http://en.wikipedia.org/wiki/Xz")

url[1]không tồn tại. Làm thế nào để người ta viết một trycatchvòng lặp (hàm) sao cho:

  1. Khi URL sai, đầu ra sẽ là: "URL web bị sai, không thể lấy".
  2. Khi URL sai, mã không dừng lại mà tiếp tục tải xuống cho đến khi kết thúc danh sách URL?

Câu trả lời:


625

Vậy thì: chào mừng bạn đến với thế giới R ;-)

Bạn đi đây

Thiết lập mã

urls <- c(
    "http://stat.ethz.ch/R-manual/R-devel/library/base/html/connections.html",
    "http://en.wikipedia.org/wiki/Xz",
    "xxxxx"
)
readUrl <- function(url) {
    out <- tryCatch(
        {
            # Just to highlight: if you want to use more than one 
            # R expression in the "try" part then you'll have to 
            # use curly brackets.
            # 'tryCatch()' will return the last evaluated expression 
            # in case the "try" part was completed successfully

            message("This is the 'try' part")

            readLines(con=url, warn=FALSE) 
            # The return value of `readLines()` is the actual value 
            # that will be returned in case there is no condition 
            # (e.g. warning or error). 
            # You don't need to state the return value via `return()` as code 
            # in the "try" part is not wrapped insided a function (unlike that
            # for the condition handlers for warnings and error below)
        },
        error=function(cond) {
            message(paste("URL does not seem to exist:", url))
            message("Here's the original error message:")
            message(cond)
            # Choose a return value in case of error
            return(NA)
        },
        warning=function(cond) {
            message(paste("URL caused a warning:", url))
            message("Here's the original warning message:")
            message(cond)
            # Choose a return value in case of warning
            return(NULL)
        },
        finally={
        # NOTE:
        # Here goes everything that should be executed at the end,
        # regardless of success or error.
        # If you want more than one expression to be executed, then you 
        # need to wrap them in curly brackets ({...}); otherwise you could
        # just have written 'finally=<expression>' 
            message(paste("Processed URL:", url))
            message("Some other message at the end")
        }
    )    
    return(out)
}

Áp dụng mã

> y <- lapply(urls, readUrl)
Processed URL: http://stat.ethz.ch/R-manual/R-devel/library/base/html/connections.html
Some other message at the end
Processed URL: http://en.wikipedia.org/wiki/Xz
Some other message at the end
URL does not seem to exist: xxxxx
Here's the original error message:
cannot open the connection
Processed URL: xxxxx
Some other message at the end
Warning message:
In file(con, "r") : cannot open file 'xxxxx': No such file or directory

Điều tra đầu ra

> head(y[[1]])
[1] "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"      
[2] "<html><head><title>R: Functions to Manipulate Connections</title>"      
[3] "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"
[4] "<link rel=\"stylesheet\" type=\"text/css\" href=\"R.css\">"             
[5] "</head><body>"                                                          
[6] ""    

> length(y)
[1] 3

> y[[3]]
[1] NA

Nhận xét bổ sung

cố gắng bắt

tryCatchtrả về giá trị liên quan đến thực thi exprtrừ khi có lỗi hoặc cảnh báo. Trong trường hợp này, các giá trị trả về cụ thể (xem return(NA)ở trên) có thể được chỉ định bằng cách cung cấp một hàm xử lý tương ứng (xem các đối số errorwarningtrong ?tryCatch). Đây có thể là các hàm đã tồn tại, nhưng bạn cũng có thể định nghĩa chúng trong tryCatch()(như tôi đã làm ở trên).

Ý nghĩa của việc chọn giá trị trả về cụ thể của các hàm xử lý

Như chúng tôi đã chỉ định NAsẽ được trả lại trong trường hợp có lỗi, phần tử thứ ba yNA. Nếu chúng ta muốn đã chọn NULLđược giá trị trả về, chiều dài của ysẽ chỉ đã 2thay vì 3như lapply()sẽ chỉ đơn giản là "bỏ qua" giá trị trả lại được NULL. Cũng lưu ý rằng nếu bạn không chỉ định giá trị trả về rõ ràng thông qua return(), các hàm xử lý sẽ trả về NULL(nghĩa là trong trường hợp có lỗi hoặc điều kiện cảnh báo).

Thông điệp cảnh báo "không mong muốn"

warn=FALSEdường như không có bất kỳ ảnh hưởng nào, một cách khác để ngăn chặn cảnh báo (trong trường hợp này không thực sự đáng quan tâm) là sử dụng

suppressWarnings(readLines(con=url))

thay vì

readLines(con=url, warn=FALSE)

Nhiều biểu thức

Lưu ý rằng bạn cũng có thể đặt nhiều biểu trong "phần biểu thức thực tế" (lập luận exprcủa tryCatch()) nếu bạn bọc chúng trong dấu ngoặc móc (giống như tôi đã minh họa trong finallyphần).


Cho rằng chuỗi đầu tiên trong các pastehàm của bạn kết thúc bằng một khoảng trắng, tại sao không bỏ qua khoảng trắng và dấu sep=""?
seancarmody

2
@seancarmody: true ;-) Tôi chỉ quen với việc kết hợp các chuỗi dài hơn / phức tạp hơn nên tôi phải kiểm soát các không gian thông qua việc thực sự viết chúng ra.
Rappster

3
Bạn nên sử dụng paste0cho điều đó!
seancarmody

6
paste0() trong căn cứ. Trong nội bộ, cả hai paste()paste0()gọi do_pastetrong paste.c . Sự khác biệt duy nhất là paste0()không vượt qua một sepđối số.
jthetzel

1
@JulienNavarre: hãy nhớ rằng "phần thử" luôn trả về đối tượng cuối cùng (hiện tại readLines(con=url, warn=FALSE)đó là điều thực tế có thể sai). Vì vậy, nếu bạn muốn thêm một tin nhắn, bạn sẽ cần lưu trữ giá trị retun thực tế trong một biến: out <- readLines(con=url, warn=FALSE)theo message("Everything worked")sau là outđể biến đây thành đối tượng cuối cùng thực sự được trả về
Rappster

69

R sử dụng các chức năng để thực hiện khối thử bắt:

Cú pháp trông giống như thế này:

result = tryCatch({
    expr
}, warning = function(warning_condition) {
    warning-handler-code
}, error = function(error_condition) {
    error-handler-code
}, finally={
    cleanup-code
})

Trong tryCatch () có hai 'điều kiện' có thể được xử lý: 'cảnh báo' và 'lỗi'. Điều quan trọng cần hiểu khi viết từng khối mã là trạng thái thực thi và phạm vi. @ nguồn


5
Thay thế error-handler-codebằngcat("web url is wrong, can't get")
seancarmody

2
bạn đã bỏ qua việc bắt tin nhắn
rawr

52

tryCatchcó cấu trúc cú pháp hơi phức tạp. Tuy nhiên, một khi chúng ta hiểu được 4 phần tạo thành một cuộc gọi tryCatch hoàn chỉnh như được hiển thị bên dưới, nó sẽ trở nên dễ nhớ:

expr : [ Bắt buộc ] Mã R được đánh giá

lỗi : [ Tùy chọn ] Điều gì sẽ chạy nếu xảy ra lỗi trong khi đánh giá mã trong expr

cảnh báo : [ Tùy chọn ] Điều gì sẽ chạy nếu cảnh báo xảy ra trong khi đánh giá mã trong expr

cuối cùng : [ Tùy chọn ] Điều gì sẽ chạy ngay trước khi thoát cuộc gọi tryCatch, bất kể nếu expr chạy thành công, có lỗi hay có cảnh báo

tryCatch(
    expr = {
        # Your code...
        # goes here...
        # ...
    },
    error = function(e){ 
        # (Optional)
        # Do this if an error is caught...
    },
    warning = function(w){
        # (Optional)
        # Do this if an warning is caught...
    },
    finally = {
        # (Optional)
        # Do this at the end before quitting the tryCatch structure...
    }
)

Do đó, một ví dụ về đồ chơi, để tính toán nhật ký của một giá trị có thể trông như sau:

log_calculator <- function(x){
    tryCatch(
        expr = {
            message(log(x))
            message("Successfully executed the log(x) call.")
        },
        error = function(e){
            message('Caught an error!')
            print(e)
        },
        warning = function(w){
            message('Caught an warning!')
            print(w)
        },
        finally = {
            message('All done, quitting.')
        }
    )    
}

Bây giờ, chạy ba trường hợp:

Một trường hợp hợp lệ

log_calculator(10)
# 2.30258509299405
# Successfully executed the log(x) call.
# All done, quitting.

Một trường hợp "cảnh báo"

log_calculator(-10)
# Caught an warning!
# <simpleWarning in log(x): NaNs produced>
# All done, quitting.

Một trường hợp "lỗi"

log_calculator("log_me")
# Caught an error!
# <simpleError in log(x): non-numeric argument to mathematical function>
# All done, quitting.

Tôi đã viết về một số trường hợp sử dụng hữu ích mà tôi sử dụng thường xuyên. Tìm thêm chi tiết tại đây: https://rsangole.netlify.com/post/try-catch/

Hy vọng điều này là hữu ích.


34

Dưới đây là một ví dụ đơn giản :

# Do something, or tell me why it failed
my_update_function <- function(x){
    tryCatch(
        # This is what I want to do...
        {
        y = x * 2
        return(y)
        },
        # ... but if an error occurs, tell me what happened: 
        error=function(error_message) {
            message("This is my custom message.")
            message("And below is the error message from R:")
            message(error_message)
            return(NA)
        }
    )
}

Nếu bạn cũng muốn chụp một "cảnh báo", chỉ cần thêm warning=tương tự như error=một phần.


1
Nên có dấu ngoặc nhọn quanh exprphần, vì có hai dòng thay vì một dòng?
Paul

Cảm ơn! Sau khi kiểm tra hai lần, tôi không thấy bất kỳ nhu cầu nào về dấu ngoặc nhọn
Paul

Cảm ơn đã kiểm tra hai lần. Khi tôi chạy mã của bạn, tôi đã nhận Error: unexpected ')' in " )"Error: unexpected ')' in " )". Thêm một cặp dấu ngoặc nhọn giải quyết vấn đề.
Paul

Đối với hầu hết các trường hợp sử dụng, bạn đã đúng, cảm ơn bạn! Nó đã được sửa chữa.
Paul

23

Vì tôi vừa mất hai ngày trong cuộc đời để cố gắng giải quyết cho tryCatch cho một chức năng khó chịu, tôi nghĩ tôi nên chia sẻ sự khôn ngoan của mình (và những gì còn thiếu). FYI - ir là một chức năng thực tế từ FinCal trong trường hợp này có lỗi trong một vài trường hợp trên một tập dữ liệu lớn.

  1. Thiết lập tryCatch như một phần của chức năng. Ví dụ:

    irr2 <- function (x) {
      out <- tryCatch(irr(x), error = function(e) NULL)
      return(out)
    }
  2. Để lỗi (hoặc cảnh báo) hoạt động, bạn thực sự cần phải tạo một hàm. Tôi ban đầu cho phần lỗi vừa viết error = return(NULL)và TẤT CẢ các giá trị trở lại null.

  3. Nhớ tạo một đầu ra phụ (như "ra" của tôi) và return(out).


3
Tại sao số 3 là cần thiết?
jan-glx
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.