如何在R Windows中将Unicode字符串写入文本文件?

9

我已经知道如何编写Unicode字符串,但仍然困惑于为什么它可以工作。

str <- "ỏ"
Encoding(str) # UTF-8
cat(str, file="no-iconv") # Written wrongly as <U+1ECF>
cat(iconv(str, to="UTF-8"), file="yes-iconv") # Written correctly as ỏ

我理解为什么“无iconv”方法行不通。这是因为cat(以及writeLines){{link1:首先将字符串转换为本地编码,然后转换为to=编码}}。在Windows上,这意味着R首先将转换为Windows-1252,但它无法理解,导致出现<U+1ECF>
我不明白的是为什么yes-iconv方法有效。如果我理解正确,这里的iconv只是返回一个使用UTF-8编码的字符串。但是,str已经是UTF-8了!为什么iconv会有任何不同呢?此外,当将iconv(str, to="UTF-8")传递给cat时,cat不应该首先转换为Windows-1252并再次搞乱一切吗?

1
我自己不了解或使用R,但仅仅通过阅读文档,“cat()”将字符串“按原样”输出,“iconv()”的“mark”参数默认为true,因此在显式调用“iconv(str,to =”UTF-8“)”之前,它的输出被标记为UTF-8。也许“str <-“ ỏ” ”没有以相同的方式标记“ str”?您可以使用“enc2utf8(str)”或“Encoding(str)<-” UTF-8“” 明确转换和标记“ str”为UTF-8,而不使用“iconv()”。这可能会对“cat()”产生影响。 - Remy Lebeau
2个回答

3
我认为在使用cat()之前,将(复制的)str的编码设置为"unknown"不仅没有魔法,而且同样有效。我认为这应该避免cat()中任何不必要的字符集转换。
以下是一个扩展示例,以演示我认为在原始示例中发生的情况:
print_info <- function(x) {
    print(x)
    print(Encoding(x))
    str(x)
    print(charToRaw(x))
}

cat("(1) Original string (UTF-8)\n")
str <- "\xe1\xbb\x8f"
Encoding(str) <- "UTF-8"
print_info(str)
cat(str, file="no-iconv")

cat("\n(2) Conversion to UTF-8, wrong input encoding (latin1)\n")
## from = "" is conversion from current locale, forcing "latin1" here
str2 <- iconv(str, from="latin1", to="UTF-8")
print_info(str2)
cat(str2, file="yes-iconv")

cat("\n(3) Converting (2) explicitly to latin1\n")
str3 <- iconv(str2, from="UTF-8", to="latin1")
print_info(str3)
cat(str3, file="latin")

cat("\n(4) Setting encoding of (1) to \"unknown\"\n")
str4 <- str
Encoding(str4) <- "unknown"
print_info(str4)
cat(str4, file="unknown")

在Windows上,R使用的是“Latin-1”区域设置(参见?l10n_info),输出文件“yes-iconv”,“latin”和“unknown”应该是正确的(字节序列0xe1、0xbb、0x8f即“ỏ”)。
在“UTF-8”区域设置下,文件“no-iconv”和“unknown”应该是正确的。
使用运行于Wine上的R 3.3.2 64位Windows版本的示例代码的输出结果如下:
(1) Original string (UTF-8)
[1] "ỏ"
[1] "UTF-8"
 chr "<U+1ECF>""| __truncated__
[1] e1 bb 8f

(2) Conversion to UTF-8, wrong input encoding (latin1)
[1] "á»\u008f"
[1] "UTF-8"
 chr "á»\u008f"
[1] c3 a1 c2 bb c2 8f

(3) Converting (2) explicitly to latin1
[1] "á»"
[1] "latin1"
 chr "á»"
[1] e1 bb 8f

(4) Setting encoding of (1) to "unknown"
[1] "á»"
[1] "unknown"
 chr "á»"
[1] e1 bb 8f

在原始的例子中,iconv()使用默认的from = ""参数,这意味着从当前语言环境进行转换,实际上是"latin1"。因为str的编码实际上是"UTF-8",所以在步骤(2)中字符串的字节表示被扭曲了,但随后由cat()隐式地恢复,当它将字符串转换回当前语言环境时,正如步骤(3)中所示的等效转换。

0

不知何故,我无法使用上述建议中的任何内容。我正在Windows系统上工作,这可能与此有关。Windows显然针对不同的语言环境具有不同的编码方式。但是我找到了Kevin Ushey的这篇优秀文章:

https://kevinushey.github.io/blog/2018/02/21/string-encoding-and-r/

他建议采用以下技巧,这对我很有效:

# Create temp file name
f <- tempfile(tmpdir = tempdir(), fileext = ".txt")

# Vector of crazy stuff
v <- c("Crazy stuff: Ω µ ", "β ¥ ∑ ", "≠ ≤ £ ∞ ؈ ლ ")

# Ensure strings are encoded as UTF-8
utf8 <- enc2utf8(v)

# Use native encoding on file connection
con <- file(f, open = "w", encoding = "native.enc")

# Use useBytes = TRUE
writeLines(utf8, con = con, useBytes = TRUE)

# Close connection
close(con)

# View results
x <- readLines(f, encoding = "UTF-8")
cat(x, sep = "\n")

# Crazy stuff: Ω µ 
# ß ¥ ∑ 
# ≠ = £ 8 ؈ ლ 

你可以看到除了无穷大符号被旋转了90度之外,其他都完美呈现。如果有人能够解决这个问题,请在评论区留言。


1
关于无穷符号,似乎R将其解释为ASCII代码56,即数字8。您可以在此处查看:utf8ToInt(“∞”)。目前还不确定如何解决这个问题。 - David J. Bosak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接