如何在R包cli的cli::cli_abort消息中保留空格?
问题:cli::cli_abort中矩阵输出空格被折叠,列对齐失效
在使用cli::cli_abort处理错误时,尝试通过capture.output打印矩阵作为错误上下文,但cli会自动折叠空格,导致矩阵列结构完全混乱。查看cli::cli_abort源码发现,它强制设置了rlang::abort(use_cli_format = TRUE),无法直接修改该参数。
可复现代码:
do_something_with_matrix <- function(x) { # 格式化矩阵为字符串向量的函数 format_mtx <- function(x, method = c("collapse", "preserve")) { method <- match.arg(method) mtx_lines <- if (method == "collapse") { c("Matrix values [Note: spaces get collapsed, hard-to-read columns]:", capture.output(x)) } else { out_txt <- gsub(" ", "_", capture.output(x)) c("Matrix values [Note: spaces replaced with '_' to prevent collapsing]:", out_txt) } names(mtx_lines) <- rep("i", length(mtx_lines)) mtx_lines } # 输入检查 if (isTRUE(any(x < 0))) { # 用下划线替代空格展示矩阵(仅作对比) cli::cli_inform(c( format_mtx(x, "preserve") )) # 抛出错误,矩阵空格被折叠 cli::cli_abort(c( "{.var x} must contain only positive values", "x" = "You've supplied a matrix with one or more negative values.", format_mtx(x) )) } } # 测试用矩阵 mtx_data <- matrix(c( 6.62, 3.33, 1.94, -4.1, 3.52, 0.5, 1.94, 0.5, 1.95), 3, 3) do_something_with_matrix(mtx_data)
运行后错误消息中的矩阵列对齐完全丢失,而用下划线替代空格的通知消息能勉强保留列结构。
解决方法
方案1:使用cli的{.verbatim}标记保留原始格式
cli支持通过{.verbatim}标记将文本按原样输出,不会折叠空格或修改格式。修改矩阵格式化函数,将矩阵内容包裹在该标记中:
do_something_with_matrix <- function(x) { format_mtx <- function(x) { mtx_lines <- capture.output(x) # 将矩阵内容组合为verbatim块 verbatim_block <- paste(mtx_lines, collapse = "\n") c( "Matrix values (preserved format):", "{.verbatim ", verbatim_block, "}" ) } if (isTRUE(any(x < 0))) { cli::cli_abort(c( "{.var x}必须仅包含正值", "x" = "你提供的矩阵包含一个或多个负值。", format_mtx(x) )) } } # 测试 do_something_with_matrix(mtx_data)
运行后错误消息中的矩阵会完全保留原始列对齐,空格不会被折叠。
方案2:手动构造带固定间距的矩阵文本
如果不想依赖{.verbatim},可以手动格式化矩阵的每一行,确保列间距固定。例如用sprintf精确控制每个元素的宽度:
do_something_with_matrix <- function(x) { format_mtx <- function(x) { # 计算每列所需的宽度(保留两位小数) col_widths <- apply(x, 2, function(col) max(nchar(sprintf("%.2f", col))) + 1) # 构造表头格式 header_str <- paste0(sprintf("%-%ds", col_widths), colnames(x) %||% paste0("[,", 1:ncol(x), "]"), collapse = "") # 构造每行内容的格式 row_str <- paste0("[%d,] ", paste0("%", col_widths, ".2f"), collapse = "") mtx_lines <- c( sprintf(" %s", header_str), apply(cbind(1:nrow(x), x), 1, function(row) do.call(sprintf, c(list(row_str), row))) ) c("Matrix values:", mtx_lines) } if (isTRUE(any(x < 0))) { cli::cli_abort(c( "{.var x}必须仅包含正值", "x" = "你提供的矩阵包含一个或多个负值。", format_mtx(x) )) } } # 测试 do_something_with_matrix(mtx_data)
这种方式能精确控制列宽,确保对齐,即使在cli消息中也能保持清晰的列结构。
方案3:直接调用rlang::abort并关闭cli格式化
如果不需要cli提供的错误样式(如感叹号、缩进等),可以直接用rlang::abort并设置use_cli_format = FALSE,这样消息会按原始文本输出:
do_something_with_matrix <- function(x) { if (isTRUE(any(x < 0))) { msg <- c( "{.var x}必须仅包含正值", "你提供的矩阵包含一个或多个负值。", "", "Matrix values:", capture.output(x) ) rlang::abort(paste(msg, collapse = "\n"), use_cli_format = FALSE) } } # 测试 do_something_with_matrix(mtx_data)
但这种方式会失去cli的样式增强,仅适合对格式要求不高的场景。
内容的提问来源于stack exchange,提问作者toni
相关产品推荐
相关产品推荐

