You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 04:36:13