如何正确处理R中的转义Unicode字符,例如em dash(—)?

10

我在处理R语言中的转义unicode字符时遇到了问题,具体来说是从MediaWiki API获取信息时遇到的问题。我会得到一个JSON字符串,例如:

{"query":{"categorymembers":[{"ns":0,"title":"Banach\u2013Tarski paradox"}]}}

这应该是完全有效的,但是通过fromJSON()读取时,我得到:

snip...
[1] "Banach\023Tarski paradox"

起初,我认为这只是RJSONIO的问题,但我在使用scan()readLines()时遇到了类似的问题。我猜想我可能缺少一些非常基础的东西。

实际上,我无法使用 R 来给出一个完全可重现的示例,因为如果我通过write()(或某个等效的函数)将“em–dash”发送到文件中, R 将自动转换 em dash。所以请看下面的步骤,创建一个名为test1的文本文件:

"em\u2013dash" "em–dash" " em \u2013 dash"

然后打开 R (根据文件路径):

> scan( file = "~/R/test1", what = "character", encoding = "UTF-8")
Read 3 items
[1] "em\\u2013dash"    "em–dash"          " em \\u2013 dash"
> readLines("~/R/test1", warn = FALSE, encoding = "UTF-8")
[1] "\"em\\u2013dash\" \"em–dash\" \" em \\u2013 dash\""

添加的转义字符是导致我在使用fromJSON()时出现问题的原因。我可以将其剥离,但这可能会在过程中破坏其他内容,而且我想象中应该有更简单的解决方案。谢谢。

以下是会话信息:

R version 2.14.1 (2011-12-22)
Platform: x86_64-apple-darwin9.8.0/x86_64 (64-bit)

locale:
[1] C/en_US.UTF-8/C/C/C/C

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] RJSONIO_0.98-0

loaded via a namespace (and not attached):
[1] tools_2.14.1

如果你遵循?scan中关于allowEscapes参数的注释,它似乎暗示解析器在scan中不支持这个参数。但是这对我来说似乎很奇怪,所以我希望我是错的。 - joran
是的,我也看到了(来自?Quotes的那一部分)。我通过连接或直接从RCurl中的getURL()读取内容时也得到了相同的结果。 - Adam Hyland
3个回答

9
这实际上不是 RJSONIO 的 bug。它预期输入的字符串已由 R 读取并处理了非 ASCII 字符。如果传入未被处理但已转义的字符串,包含 \u,会发生错误。在我的机器上,区域设置为 en_US.UTF-8,命令为:
fromJSON('{"query":{"categorymembers":[{"ns":0,"title":"Banach\u2013Tarski paradox"}]}}')

产生
$query
$query$categorymembers
$query$categorymembers[[1]]
$query$categorymembers[[1]]$ns
[1] 0

$query$categorymembers[[1]]$title
[1] "Banach–Tarski paradox"

请注意字符前缀为 \u 而不是 \\u。当你简单地输入该字符串时,请查看它在 R 中的显示效果。
因此,问题在于在 fromJSON() 之前出现了 \u。我可能会在 RJSONIO 中添加支持以处理这些未经处理的字符串。

谢谢回复,Duncan。我可以看到如果我使用你的字符串,它确实会产生我想要的结果。但我不知道如何通过RCurl或readLines读取的内容实现这一点。尽管如此,这在很大程度上是学术性的,因为我已经转向了rjson,并且它可以正常工作。 - Adam Hyland
我很想了解更多关于如何处理RJSONIO中的问题。我有一些带有\\u前缀的字符串,但不确定在传递给fromJSON之前如何处理它们。 - daroczig

5

正如您可以清楚地看到的那样,这是RJSONIO中的一个错误:

> RJSONIO::fromJSON('{"x":"foo\\u2013bar"}')
           x 
"foo\023bar" 

rjson中它的效果非常好:

> rjson::fromJSON('{"x":"foo\\u2013bar"}')
$x
[1] "foo–bar"

并且为了证明它是正确的值:

 > Sys.setlocale("LC_ALL", "C")
[1] "C/C/C/C/C/en_US.UTF-8"
> rjson::fromJSON('{"x":"foo\\u2013bar"}')
$x
[1] "foo<U+2013>bar"

在您的分析中,您可能会被打印字符串与实际字符串所混淆。 print 命令用于打印字符串 - 如果您想查看实际字符串,可以使用 catcharToRaw 命令。此外,scan 命令不会解释任何转义字符,因此你输入什么就会输出什么。


1
我认为根本问题在于RJSONIO中未启用libjson选项JSON_UNICODE。但是,当输入编码为UTF-8时,问题似乎并未显现:
library(RJSONIO)
x = "北京填鴨们"
identical(x, fromJSON(toJSON(x)))
# [1] TRUE

只有在输入使用 JSON 转义字符时才会出现问题。在这些情况下,RJSONIO 似乎会生成 latin1 输出,但未正确设置编码。
x <- fromJSON('["Z\\u00FCrich"]')
print(x)
# [1] "Z\xfcrich"

nchar(x)
#Error in nchar(x) : invalid multibyte string 1

对于这个简单的例子,我们可以通过手动将编码设置为latin1来解决它:

#Set the correct encoding
Encoding(x) <- "latin1"
print(x)
#[1] "Zürich" 

然而,对于不属于latin1字符集的字符,这当然是行不通的:

#This should be: "填"
fromJSON('["\\u586B"]')

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