抑制但捕获 R 中的警告

Posted

技术标签:

【中文标题】抑制但捕获 R 中的警告【英文标题】:Suppress but capture warnings in R 【发布时间】:2018-04-18 21:34:20 【问题描述】:

我正在构建一个函数来连接到一个特定的受密码保护的 ODBC 数据源,该数据源将被团队中的许多成员使用 - 它可以在多个环境中使用。如果连接被拒绝,我想显示警告消息但屏蔽显示的密码。如果我使用suppressWarnings(),据我所知,没有任何内容被捕获,如果我不使用,则该消息将显示在标准输出中并带有密码。到目前为止的功能如下:

connectToData <- function(uid, pswd, dsn='myDSN') 
  # Function to connect to myDSN data
  #
  # Args:
  #   uid: The user's ID for connecting to the database 
  #   pswd: The user's password for connecting to the database. 
  #   dsn: The DSN for the (already existing) ODBC connection to the 5G  
  #        data. It must be set up on an individual Windows user's machine, 
  #        and they could use any name for it. The default is 'myDSN' 
  #
  # Returns:
  #   The 'RODBC' class object returned by the RODBC:odbcConnect() function.
  #
  # TODO: 1) See if you can specify the connection using odbcDriverConnect()
  #          so as to not rely on user's ODBC connections
  #       2) Capture warnings from odbcConnect() and print them while 
  #          disguising password using gsub, as I've attempted to do below.
  library('RODBC')

  db.conn <- odbcConnect(dsn,
                         uid=uid,
                         pwd=pswd)

  if(class(db.conn) != 'RODBC')   # Error handling for connections that don't make it
    print(gsub(pswd,'******',warnings()))  # This doesn't work like I want it to
    stop("ODBC connection could not be opened. See warnings()")
   else 
    return(db.conn)
  

当我使用正确的用户名/密码运行它时,我会得到正确的结果,但是当我使用错误的密码运行它时,我会得到:

> db.conn <- connectTo5G(uid='myID',pswd='badpassword', dsn='myDSN') 
[1] "RODBC::odbcDriverConnect(\"DSN=myDSN;UID=myID;PWD=******\")" 
[2] "RODBC::odbcDriverConnect(\"DSN=myDSN;UID=myID;PWD=******\")" 
Error in connectTo5G(uid = "myID", pswd = "badpassword", dsn = "myDSN") :    
ODBC connection could not be opened. See warnings() 
In addition: Warning messages: 
1: In RODBC::odbcDriverConnect("DSN=myDSN;UID=myID;PWD=badpassword") :  
[RODBC] ERROR: state 28000, code 1017, message [Oracle][ODBC][Ora]ORA-01017: invalid username/password; logon denied
2: In RODBC::odbcDriverConnect("DSN=myDSN;UID=myID;PWD=badpassword") :
ODBC connection failed

print(gsub(...)) 似乎可以处理调用函数之前的最新警告,并且它也只打印产生警告的函数调用,而不是警告的文本​​。

我想做的是在“另外:警告消息:”之后捕获所有内容,以便我可以在上面使用gsub(),但避免在gsub() 有机会处理它之前打印它。我想我需要使用withCallingHandlers(),但我查看了文档和示例,但我无法弄清楚。

一些额外的背景:这是一个 Oracle 数据库,它会在用户尝试连接三次后锁定用户,所以我想使用 stop(),以防有人编写多次调用此函数的代码。我小组中的不同用户同时在 Windows 和 Linux 中工作(有时来回切换),因此任何解决方案都需要灵活。

【问题讨论】:

也许更传统的错误处理工具在这里会更好用,比如tryCatch? (小点,inherits() 通常是检查对象 S3 类向量的首选习惯用法,而不是 ==!= this 是否有助于弄清楚如何使用 withCallingHandlers 来捕获错误和警告? 谢谢,@joran。我最终使用了inherits(),但使用了TryCatch(),如下面@JonGrub 的回答。 【参考方案1】:

捕捉错误信息

我不完全理解您想用 ODBC 完成什么,但在转换错误消息方面,您可以按照 @joran 的建议使用 tryCatch

pswd = 'badpassword'
# Just as a reproducable example, a function which fails and outputs badpassword
failing <- function()
  badpassword == 1

# This would be the error handling part
tryCatch(failing(),
         error = function(e) gsub(pswd, '******', e))
[1] "Error in failing(): object '******' not found\n"

e 在这种情况下是错误消息,您可以考虑其他方法来操纵屏幕上的内容,因此根据替换的内容猜测密码并不容易。请注意,例如,如果密码由于某种原因是“对象”,“对象”也会被替换。甚至是单词的一部分,它们也会被替换。至少,在 gsub 命令中包含单词边界是有意义的:

pswd = 'ling'
failing <- function()
  ling == 1

tryCatch(failing(),
         error = function(e) gsub(paste0("\\b", pswd, "\\b"), '******', e))
[1] "Error in failing(): object '******' not found\n"

对于其他改进,您应该仔细查看具体的错误消息。

警告

trycatch也可以操纵警告:

pswd = 'ling'
failing <- function()
  warning("ling")
  ling == 1

tryCatch(failing(),
         warning = function(w) gsub(paste0("\\b", pswd, "\\b"), '******', w),
         error = function(e) gsub(paste0("\\b", pswd, "\\b"), '******', e))
[1] "simpleWarning in failing(): ******\n"

然而,这不会显示错误。

withCallingHandlers

如果你真的想从错误和警告中捕获所有输出,你确实需要withCallingHandlers,它的工作方式大致相同,只是它不会终止其余的评估。

pswd = 'ling'
failing <- function(pswd)
  warning(pswd)
  warning("asd")
  stop(pswd)

withCallingHandlers(failing(),
                    warning = function(w) 
                      w <- gsub(paste0("\\b", pswd, "\\b"), '******', w)
                      warning(w),
                    error = function(e)
                      e <- gsub(paste0("\\b", pswd, "\\b"), '******', e)
                      stop(e)
                    )

【讨论】:

这看起来有效,谢谢。我目前被锁定在数据库之外,因此我无法确认它是否正确处理了成功的连接,但看起来会。这就是我最终的结果:db.conn &lt;- tryCatch(odbcConnect(dsn, uid=uid, pwd=pswd), warning = function(w) print(gsub(paste0("\\b", pswd, "\\b"), '******', w)), error = function(e) print(gsub(paste0("\\b", pswd, "\\b"), '******', e)))

以上是关于抑制但捕获 R 中的警告的主要内容,如果未能解决你的问题,请参考以下文章

抑制 Xcode 中的运行时自动布局警告

在 Perl 中抑制无法定位模块警告

正确抑制数据表中的警告?

抑制预期 Oracle 异常的 PHP 警告

抑制一行中的多个警告?

如何在 Kotlin 中抑制检查式警告