Unicode 字符输出与 print() 的差异
Differences in Unicode character output with print()
我在 Windows 上遇到了另一个 R 的数据帧和 Unicode 字符之间的奇怪交互,这次涉及 knitr 和 rmarkdown。
隐式打印正常工作
我正在尝试根据包含 Unicode 字符的数据框打印 HTML table,如以下简单示例所示:
---
title: "Unicode Print Test"
---
```{r, results='asis'}
library(knitr)
knitr::kable(data.frame(eta="\U03B7"), format="html")
```
当文档编织成 HTML 时,这会产生我想要的输出,如下所示:
显式打印不会
但在实际应用中,我需要从 for
循环中打印几个 table,这意味着我必须明确地 print()
table:
```{r, results='asis'}
library(knitr)
x <- knitr::kable(data.frame(eta="\U03B7"), format="html")
print(x)
```
现在,当文档编织成 HTML:
时,Unicode 字符打印不正确
怎么办?
为什么会出现隐式打印和显式打印之间的这种差异?至少在 R 控制台中执行时,显式和隐式打印都会调用 knitr:::print.knitr_kable()
函数。我猜它与 evalaute
函数(来自同名包)有关,它实际上在 knitr 代码块中执行代码,但我不知道是什么。
有什么方法可以进行显式 print()
调用并获得格式正确的输出?我知道这个 locale workaround 似乎适用于其他一些 Unicode + Data Frame 问题,但不适用于这个问题。
编辑:根据一位知识渊博的评论者的说法,这是一个深层次的问题,涉及 R 在显示之前如何以及何时使用本机 Windows 编码转换字符。因此,在 Windows 上使用 print
函数时,这总是会成为一个问题,除非基数 R 发生显着变化。
更新的问题:是否有任何其他方法(除了 print()
-ing 之外)让 kable
对象从内部表达式(例如 for 循环)显示?
会话信息()
## R version 3.5.1 (2018-07-02)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 7 x64 (build 7601) Service Pack 1
##
## Matrix products: default
##
## locale:
## [1] LC_COLLATE=English_United States.1252
## [2] LC_CTYPE=English_United States.1252
## [3] LC_MONETARY=English_United States.1252
## [4] LC_NUMERIC=C
## [5] LC_TIME=English_United States.1252
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] knitr_1.20
##
## loaded via a namespace (and not attached):
## [1] compiler_3.5.1 backports_1.1.2 magrittr_1.5 rprojroot_1.3-2
## [5] tools_3.5.1 htmltools_0.3.6 yaml_2.2.0 Rcpp_0.12.18
## [9] stringi_1.1.7 rmarkdown_1.10 highr_0.7 stringr_1.3.1
## [13] digest_0.6.16 evaluate_0.11
因此,显然这个字符转换问题不太可能在不久的将来自行解决,可能只会在 OS 级别解决。但是根据@YihuiXie 在评论中提出的优秀建议,有两种方法可以解决这个问题。最佳解决方案将取决于您在其中创建 table 的上下文。
场景 1:仅表
如果您需要从 for 循环内部输出的唯一类型的对象是 tables,那么您可以在循环内的列表中累积 kable
对象,然后折叠在循环结束时将 kable
的列表转换为单个字符向量,并使用 knitr::asis_output
显示它。
```{r, results="asis"}
library(knitr)
character_list <- list(eta="\U03B7", sigma="\U03C3")
kable_list <- vector(mode="list", length = length(character_list))
for (i in 1:length(character_list)) {
kable_list[[i]] <- knitr::kable(as.data.frame(character_list[i]),
format="html"
)
}
knitr::asis_output(paste(kable_list, collapse = '\n'))
```
这会在 HTML 文档中生成以下 table:
场景 2:表格和其他对象(例如绘图)
如果您在 for 循环的每次迭代中同时输出 tables 和其他对象(例如绘图),则上述解决方案将不起作用 - 您无法将绘图强制转换为特征向量!在这一点上,我们必须通过编写自定义的 knitr 输出挂钩来对 kable
输出进行一些 post 处理。
基本方法是用等效的 HTML 实体替换 table 单元格中的破坏序列。请注意,因为 table 是在 results="asis"
块中创建的,所以我们必须覆盖 chunk
级别的输出挂钩,而不是 output
级别的输出挂钩(令人困惑,我知道) .
```{r hook_override}
library(knitr)
default_hook <- knit_hooks$get("chunk")
knit_hooks$set(chunk = function(x, options) {
# only attempt substitution if output is a character vector, which I *think* it always should be
if (is.character(x)) {
# Match the <U+XXXX> pattern in the output
match_data <- gregexpr("<U\+[0-9A-F]{4,8}>", x)
# If a match is found, proceed with HTML entity substitution
if (length(match_data[[1]]) >= 1 && match_data[[1]][1] != -1) {
# Extract the matched strings from the output
match_strings <- unlist(regmatches(x, match_data))
# Extract the hexadecimal Unicode sequences from inside the <U > bracketing
code_sequences <- unlist(regmatches(match_strings,
gregexpr("[0-9A-F]{4,8}", match_strings)
)
)
# Replace any leading zero's with x, which is required for the HTML entities
code_sequences <- gsub("^0{1,4}", "x", code_sequences)
# Slap the &# on the front, and the ; on the end of each code sequence
regmatches(x, match_data) <- list(paste0("&#", code_sequences, ";"))
}
}
# "Print" the output
default_hook(x, options)
})
```
```{r tables, results="asis"}
character_list <- list(eta="\U03B7", sigma="\U03C3")
for (i in 1:length(character_list)) {
x <- knitr::kable(as.data.frame(character_list[i]),
format="html"
)
print(x)
}
```
```{r hook_reset}
knit_hooks$set(chunk = default_hook)
```
这会在 HTML 文档中生成以下 tables:
请注意,这次 sigma 不像第一个示例中那样显示为 σ,而是显示为 s!这是因为 sigma 在到达块输出挂钩之前被转换为 s ! 我不知道如何阻止 that 发生。如果您愿意,请随时发表评论 =)
我还意识到使用正则表达式在 HTML table 中进行替换可能很脆弱。如果这种方法碰巧对你的用例失败,也许使用 rvest
包来单独解析每个 table 单元格会更健壮。
我在 Windows 上遇到了另一个 R 的数据帧和 Unicode 字符之间的奇怪交互,这次涉及 knitr 和 rmarkdown。
隐式打印正常工作
我正在尝试根据包含 Unicode 字符的数据框打印 HTML table,如以下简单示例所示:
---
title: "Unicode Print Test"
---
```{r, results='asis'}
library(knitr)
knitr::kable(data.frame(eta="\U03B7"), format="html")
```
当文档编织成 HTML 时,这会产生我想要的输出,如下所示:
显式打印不会
但在实际应用中,我需要从 for
循环中打印几个 table,这意味着我必须明确地 print()
table:
```{r, results='asis'}
library(knitr)
x <- knitr::kable(data.frame(eta="\U03B7"), format="html")
print(x)
```
现在,当文档编织成 HTML:
时,Unicode 字符打印不正确怎么办?
为什么会出现隐式打印和显式打印之间的这种差异?至少在 R 控制台中执行时,显式和隐式打印都会调用 knitr:::print.knitr_kable()
函数。我猜它与 evalaute
函数(来自同名包)有关,它实际上在 knitr 代码块中执行代码,但我不知道是什么。
有什么方法可以进行显式 print()
调用并获得格式正确的输出?我知道这个 locale workaround 似乎适用于其他一些 Unicode + Data Frame 问题,但不适用于这个问题。
编辑:根据一位知识渊博的评论者的说法,这是一个深层次的问题,涉及 R 在显示之前如何以及何时使用本机 Windows 编码转换字符。因此,在 Windows 上使用 print
函数时,这总是会成为一个问题,除非基数 R 发生显着变化。
更新的问题:是否有任何其他方法(除了 print()
-ing 之外)让 kable
对象从内部表达式(例如 for 循环)显示?
会话信息()
## R version 3.5.1 (2018-07-02)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 7 x64 (build 7601) Service Pack 1
##
## Matrix products: default
##
## locale:
## [1] LC_COLLATE=English_United States.1252
## [2] LC_CTYPE=English_United States.1252
## [3] LC_MONETARY=English_United States.1252
## [4] LC_NUMERIC=C
## [5] LC_TIME=English_United States.1252
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] knitr_1.20
##
## loaded via a namespace (and not attached):
## [1] compiler_3.5.1 backports_1.1.2 magrittr_1.5 rprojroot_1.3-2
## [5] tools_3.5.1 htmltools_0.3.6 yaml_2.2.0 Rcpp_0.12.18
## [9] stringi_1.1.7 rmarkdown_1.10 highr_0.7 stringr_1.3.1
## [13] digest_0.6.16 evaluate_0.11
因此,显然这个字符转换问题不太可能在不久的将来自行解决,可能只会在 OS 级别解决。但是根据@YihuiXie 在评论中提出的优秀建议,有两种方法可以解决这个问题。最佳解决方案将取决于您在其中创建 table 的上下文。
场景 1:仅表
如果您需要从 for 循环内部输出的唯一类型的对象是 tables,那么您可以在循环内的列表中累积 kable
对象,然后折叠在循环结束时将 kable
的列表转换为单个字符向量,并使用 knitr::asis_output
显示它。
```{r, results="asis"}
library(knitr)
character_list <- list(eta="\U03B7", sigma="\U03C3")
kable_list <- vector(mode="list", length = length(character_list))
for (i in 1:length(character_list)) {
kable_list[[i]] <- knitr::kable(as.data.frame(character_list[i]),
format="html"
)
}
knitr::asis_output(paste(kable_list, collapse = '\n'))
```
这会在 HTML 文档中生成以下 table:
场景 2:表格和其他对象(例如绘图)
如果您在 for 循环的每次迭代中同时输出 tables 和其他对象(例如绘图),则上述解决方案将不起作用 - 您无法将绘图强制转换为特征向量!在这一点上,我们必须通过编写自定义的 knitr 输出挂钩来对 kable
输出进行一些 post 处理。
基本方法是用等效的 HTML 实体替换 table 单元格中的破坏序列。请注意,因为 table 是在 results="asis"
块中创建的,所以我们必须覆盖 chunk
级别的输出挂钩,而不是 output
级别的输出挂钩(令人困惑,我知道) .
```{r hook_override}
library(knitr)
default_hook <- knit_hooks$get("chunk")
knit_hooks$set(chunk = function(x, options) {
# only attempt substitution if output is a character vector, which I *think* it always should be
if (is.character(x)) {
# Match the <U+XXXX> pattern in the output
match_data <- gregexpr("<U\+[0-9A-F]{4,8}>", x)
# If a match is found, proceed with HTML entity substitution
if (length(match_data[[1]]) >= 1 && match_data[[1]][1] != -1) {
# Extract the matched strings from the output
match_strings <- unlist(regmatches(x, match_data))
# Extract the hexadecimal Unicode sequences from inside the <U > bracketing
code_sequences <- unlist(regmatches(match_strings,
gregexpr("[0-9A-F]{4,8}", match_strings)
)
)
# Replace any leading zero's with x, which is required for the HTML entities
code_sequences <- gsub("^0{1,4}", "x", code_sequences)
# Slap the &# on the front, and the ; on the end of each code sequence
regmatches(x, match_data) <- list(paste0("&#", code_sequences, ";"))
}
}
# "Print" the output
default_hook(x, options)
})
```
```{r tables, results="asis"}
character_list <- list(eta="\U03B7", sigma="\U03C3")
for (i in 1:length(character_list)) {
x <- knitr::kable(as.data.frame(character_list[i]),
format="html"
)
print(x)
}
```
```{r hook_reset}
knit_hooks$set(chunk = default_hook)
```
这会在 HTML 文档中生成以下 tables:
请注意,这次 sigma 不像第一个示例中那样显示为 σ,而是显示为 s!这是因为 sigma 在到达块输出挂钩之前被转换为 s ! 我不知道如何阻止 that 发生。如果您愿意,请随时发表评论 =)
我还意识到使用正则表达式在 HTML table 中进行替换可能很脆弱。如果这种方法碰巧对你的用例失败,也许使用 rvest
包来单独解析每个 table 单元格会更健壮。