Как правильно обрабатывать экранированные символы Unicode в R, например, em dash (-)

У меня проблемы с обработкой экранированных символов Unicode в R, особенно те, которые встречаются при получении информации из MediaWiki API. Я бы нашел строку JSON, как

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

Который должен быть совершенно действительным, но при прочтении fromJSON() Я получил:

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

Сначала я думал, что это просто проблема с RJSONIO, но я сталкиваюсь с подобными проблемами с scan() а также readLines(), Я предполагаю, что мне не хватает чего-то очень простого.

На самом деле я не могу привести полностью воспроизводимый пример, используя только R, потому что, если я отправлю "em\u2013dash" в файл с помощью write() (или какой-либо эквивалентной функции), 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\""

Добавленный escape-символ - вот что вызывает у меня проблемы с 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

3 ответа

Решение

Это ошибка в 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 цитирует его содержимое для печати - если вы хотите увидеть фактическую строку, вы можете использовать cat или же charToRaw, Также scan не интерпретирует побеги, поэтому вы получаете то, что даете.

На самом деле это не ошибка в RJSONIO. Он предназначен для ожидания строки, которая была прочитана 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 для обработки таких необработанных строк.

Я думаю, что основная проблема заключается в том, что libjson вариант JSON_UNICODE не включен в RJSONIO, Однако кажется, что проблема не проявляется, когда ввод 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"]')
Другие вопросы по тегам