我有一个基于artemklevtsov/r-alpine:latest
的docker容器设置。当我运行R
脚本时,我看到了这个错误:
Invalid encoding UTF-8: defaulting to UTF-8.
我在httr库中跟踪了这段代码: https://github.com/hadley/httr/blob/master/R/content-parse.r#L5
在高山回归编码上看起来像iconvlist()
,在末尾有一个尾随逗号,例如:
iconvlist()
[1] "..." "ISO8859-1," "ISO8859-2," "ISO8859-3," "ISO8859-4,"
[6] "ISO8859-5," "ISO8859-6," "ISO8859-7," "UCS-2BE," "UCS-2LE,"
[11] "US_ASCII," "UTF-16BE," "UTF-16LE," "UTF-32BE," "UTF-8,"
因此UTF-8
永远不会匹配UTF-8,
。有没有人遇到过这个问题?我在本地Mac(OSX)上获得的编码列表是正确的,没有逗号。它也不会发生在CentOS上,所以它看起来像是高山特有的。
有没有办法解决这个问题?可能通过R
中的配置或修改iconvlist()
输出?
答案 0 :(得分:2)
这次,我遇到了同样的问题,即调用read::read_csv
,后者使用base::iconvlist
并给出相同的错误消息Invalid encoding UTF-8: defaulting to UTF-8.
。这是在alpine:3.12
上使用apk add R
提供的R 3.6.3,基于以下详细信息,我认为该问题将出现在任何版本的alpine和R上,除非已采取步骤直接解决该问题。
我找到了几种解决方案。 TLDR:
system.file("iconvlist", package = "utils")
的文件中删除逗号,或者iconv
支持。 base::iconvlist()
函数使用iconvlist
文件作为后备方法来获取系统支持的编码列表。在高山上,出于以下概述的原因,将始终使用此后备方法,但是iconvlist
文件中包含逗号,R对此不期望。
最简单的解决方案是从iconvlist
文件中删除逗号(可以在base::system.file()
中找到)。
> system.file("iconvlist", package = "utils")
[1] "/usr/lib/R/library/utils/iconvlist"
从命令行(不是R)中删除逗号的一种方法是:
sed -i 's/,//g' /usr/lib/R/library/utils/iconvlist
随后对base::iconvlist()
的调用将读取并解析新文件而没有逗号,而依赖于base::iconvlist()
的其他函数将能够成功检查支持,例如表示“ UTF-8”。
> iconvlist()
[1] "..." "ISO8859-1" "ISO8859-2" "ISO8859-3" "ISO8859-4" "ISO8859-5"
[7] "ISO8859-6" "ISO8859-7" "UCS-2BE" "UCS-2LE" "US_ASCII" "UTF-16BE"
[13] "UTF-16LE" "UTF-32BE" "UTF-8" "UTF32-LE" "WCHAR_T"
> "UTF-8" %in% iconvlist()
[1] TRUE
字符编码的国际转换(iconv
是R Administration and Installation Manual中规定的R期望由操作系统提供的功能。操作系统提供自己的iconv
功能实现,有时功能更少。由于阿尔卑斯山的设计极小,因此仅提供满足POSIX标准所需的东西就不足为奇了。
当R构建在系统上时,它首先检查主机C开发库对iconv
的支持程度,然后再将功能编译到R的内部。至关重要的是,检查对 C函数 iconvlist
的支持,这在高山上是不存在的,如R:checking for iconvlist... no
的apk build log所示,因此该C函数在内部R不可用。
R的base::iconvlist()
函数将首先尝试通过.Internal(iconv(...
使用预编译的C代码获取编码,如果可用,它将调用iconvlist
(在C中)。由于高山上不存在iconvlist
C函数,因此此.Internal
调用将始终返回NULL,而R函数将回退以从iconvlist
文件中读取信息:
> iconvlist
function ()
{
int <- .Internal(iconv(NULL, "", "", "", TRUE, FALSE))
if (length(int))
return(sort.int(int))
icfile <- system.file("iconvlist", package = "utils")
# ... (truncated)
iconvlist
文件的格式意外?当R为built时,通过列出可用编码的命令iconvlist
创建iconv -l
文件。这是/usr/bin/iconv
中不是iconv -l
的输出格式没有标准。 Alpine尝试符合POSIX标准,而这些仅要求-l
选项将“值以未指定的格式写入标准输出”。
R期望文件格式包含用空格分隔的值(base::iconvlist()
用strsplit(ext, "[[:space:]]")
解析文件),这对于其他Linux变体(例如, Debian,CentOS,但不适用于带有逗号的alpine musl libc version。
更严格的解决方案是使用提供iconv
C函数的备用iconvlist
C库实现从源代码构建R。 base::iconvlist()
然后可以通过其.Internal(iconv(...
调用来获取编码,而无需回退到iconvlist
文件。
提供iconvlist
的实现是GNU libiconv,该实现已打包为alpine并可以通过以下方式安装:
apk add gnu-libiconv gnu-libiconv-dev
软件包gnu-libiconv-dev
在/usr/include/gnu-libiconv/
中提供了标头,因此这里需要优先指向编译器,而不是/usr/include
中的现有编译器。这超出了我的专业知识,但是可以通过在-I/usr/include/gnu-libiconv
环境变量中添加CFLAGS
来完成。
export CFLAGS=-I/usr/include/gnu-libiconv $CFLAGS
运行./configure
会产生类似于以下内容的检查结果:
... (truncated)
checking for iconv.h... yes
checking for iconv... in libiconv
checking whether iconv accepts "UTF-8", "latin1", "ASCII" and "UCS-*"... yes
checking whether iconv accepts "CP1252"... yes
checking for iconvlist... yes
... (truncated)
在make
之后,我可以运行./bin/R
,即使iconvlist
文件仍然包含逗号,对base::iconvlist()
的调用也会产生格式正确的结果:
> iconvlist()
[1] "850"
[2] "862"
[3] "866"
[4] "ANSI_X3.4-1968"
[5] "ANSI_X3.4-1986"
... (truncated)
# The unsorted list is coming from the internal C functions, not the file
> .Internal(iconv(NULL, "", "", "", TRUE, FALSE))
[1] "ANSI_X3.4-1968"
[2] "ANSI_X3.4-1986"
[3] "ASCII"
[4] "CP367"
[5] "IBM367"
... (truncated)