Как правильно обрабатывать экранированные символы 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"]')