解决R包Check汇报'marked UTF-8 strings'问题
王诗翔 · 2021-03-24
今天在处理 UCSCXenaShiny 的 R 包 check 时发现报出 Note: found 162 marked UTF-8 strings
这种字符串编码问题(具体 action 报告)。
❯ checking installed package size ... NOTE
installed size is 6.1Mb
sub-directories of 1Mb or more:
doc 1.7Mb
shinyapp 3.2Mb
❯ checking data for non-ASCII characters ... NOTE
Note: found 162 marked UTF-8 strings
0 errors ✔ | 0 warnings ✔ | 2 notes ✖
我网上搜索了下然后确定了 DESCRIPTION 文件里已经显式指定了代码文件是 UTF-8 编码,为什么会出这种问题呢?继续搜索发现该问题是出在包中引入的数据对象上,就是我们放在包里的数据存在编码问题。
目前这个我处理的包有 10 个数据集,为了锁定问题源,我采用了 https://github.com/dankelley/oce/issues/1663 提到的策略,即逐步删除 data/
目录下的文件,然后检查是否该问题还存在。
于是,我写了下面一段代码:
devtools::load_all()
data_files <- dir("data")
for (i in data_files) {
message("Removing file ", i)
unlink(file.path("data", i))
print(tools:::.check_package_datasets("."))
}
发现然并卵,for 循环一直报 Note: found 162 marked UTF-8 strings
。我就纳闷了,最后数据文件都删完了,居然还会出这个 NOTE?
我仔细思考🤔了下,是否 tools:::.check_package_datasets()
一直使用的是已经缓存到内存的包?也就是说,虽然我已经删除了文件,但整个包可以已经载入内存,所以无论是否删除这 10 个数据文件,函数内部依旧可以获取到这些数据的信息。通过 debug(tools:::.check_package_datasets)
我发现事情不是我想的这样,该函数内部会检查 data/
下的文件并获取文件列表用于载入,本人功底不足,加上 RStudio 对这种特别底层的调试支持不怎么爽利,我没有找到它怎么输出 NOTE
结果的,于是弃疗了该策略。
不过在调试中我发现 Encoding()
函数可以获取字符串编码信息:
Encoding("abc")
#> [1] "unknown"
所以我尝试载入数据对它的列进行编码查询,发现都是 “unknown”。既然包检查想要的是 ASCII 编码,那我转换一下不就完了?
我在网络上搜索了 3 种方法,第 1 种没有作用。
- 使用
iconv()
函数,用法如下:
nonUTF <- iconv(df$TroubleVector, from="UTF-8", to="ASCII")
参考的问答
这里的问题是 from
参数不支持 unknown
,所以无用。
- 使用 2 次转换的方法,先转为 Raw 类型,然后再转回来。
df %>%
mutate_if(is.character, function(x){
x %>%
sapply(function(y){
y %>%
charToRaw %>%
rawToChar
})
})
参考的问答
- 使用 stringi 包中的
stringi::stri_enc_toascii()
函数(推荐),配合stringi::stri_enc_isascii()
使用:
a <- "Sympathetic\xcaNervous System"
a
#> [1] "Sympathetic\xcaNervous System"
stringi::stri_enc_isascii(a)
#> [1] FALSE
b <- stringi::stri_enc_toascii(a)
b
#> [1] "Sympathetic\032Nervous System"
stringi::stri_enc_isascii(b)
#> [1] TRUE
利用第 3 种方法,我首先将问题锁定到了某一个数据:
is_asc <- function(x) {
x <- stringi::stri_enc_isascii(x)
if (FALSE %in% x) {
return(FALSE)
} else {
return(TRUE)
}
}
FALSE %in% sapply(TCGA.organ, is_asc)
FALSE %in% sapply(ccle_absolute, is_asc)
FALSE %in% sapply(ccle_info, is_asc)
FALSE %in% sapply(tcga_clinical, is_asc)
FALSE %in% sapply(tcga_genome_instability, is_asc)
FALSE %in% sapply(tcga_gtex, is_asc)
FALSE %in% sapply(tcga_purity, is_asc)
FALSE %in% sapply(tcga_subtypes, is_asc)
FALSE %in% sapply(tcga_surv, is_asc)
FALSE %in% sapply(toil_info, is_asc)
sapply(toil_info, is_asc)
然后锁定问题列以及检查问题字符串总数是否能对上:
> sapply(toil_info, is_asc)
sample detailed_category primary disease or tissue _primary_site
TRUE TRUE TRUE FALSE
_sample_type _gender _study
TRUE TRUE TRUE
> length(which(!stringi::stri_enc_isascii(toil_info$`_primary_site`)))
[1] 162
找到具体问题位置就简单了,转换保存即可:
toil_info$`_primary_site` <- stringi::stri_enc_toascii(toil_info$`_primary_site`)
提交的 gh action 也显示问题解决了:
❯ checking installed package size ... NOTE
installed size is 6.1Mb
sub-directories of 1Mb or more:
doc 1.7Mb
shinyapp 3.2Mb
0 errors ✔ | 0 warnings ✔ | 1 note ✖
最后回顾一下,为什么逐步删除的方法不起作用呢???
我还是道行太浅了~