Проблемы с кодировкой при использовании Rscript через launchd

Я столкнулся с неожиданной проблемой при попытке запланировать R-скрипт с помощью launchd: используя Rgui или Rscript в терминале (Mac OS X 10.7.5), скрипт работает без проблем, но когда скрипт запускается с помощью launchd, кажется, есть проблема с кодировкой.

В качестве примера можно использовать этот скрипт для создания облака слов из RSS-канала журнала Le Monde:

#!/usr/bin/Rscript
require(wordcloud)
require(tm)
require(XML)
titles <- xpathSApply(htmlParse("http://www.lemonde.fr/rss/une.xml"),"//item/title",xmlValue)
titles <- gsub("[[:punct:]]"," ",titles)
rss <- Corpus(VectorSource(titles),readerControl=list(language="fr"))
rss <- tm_map(rss, stripWhitespace)
rss <- tm_map(rss, function(x)removeWords(x,stopwords("fr")))
tdm <- TermDocumentMatrix(rss)
m <- as.matrix(tdm)
v <- sort(rowSums(m),decreasing=TRUE)
d <- data.frame(word = names(v),freq=v)
png("/path/to/wordcloud.png",w=5,h=5,units="in",res=100)
par(mar=c(0,0,0,0))
wordcloud(d$word,d$freq,scale=c(3,.1),min.freq=2)
dev.off()

Получив разрешения с помощью chmod +x, если я запускаю скрипт через Rgui или через терминал, я получаю что-то вроде этого:

введите здесь описание изображения

Но если я создам LaunchAgent для планирования запуска этого скрипта в заданный интервал времени с помощью файла plist, подобного этому:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>test</string>
        <key>ProgramArguments</key>
        <array>
                <string>/path/to/test.R</string>
        </array>
</dict>
</plist>

Затем загрузите его и запустите:

launchctl load ~/Library/LaunchAgents/test.plist
launchctl start test

Вот что я получаю:

введите здесь описание изображения

Итак, я думаю, мои вопросы таковы:
- Почему это?
- Как обойти это?

Редактировать
После комментария @hrbrmstr я вставил в код строку writeLines(capture.output(Sys.getenv()), con="/tmp/launchenv.txt").
Основное различие между содержимым Sys.getenv() заключается в том, что тот, который соответствует Rgui, содержит разные R_PLATFORM из двух другие, и R_LIBS, в то время как у двух других были DYLD_LIBRARY_PATH и R_DEFAULT_PACKAGES.
Единственное, что общего у Rgui и терминала, но отличается в выводе от launchd, это то, что PATH содержал /usr/local/bin (это папка, которая не на самом деле существуют на моем компьютере) вдобавок ко всему прочему. Тем не менее я попытался запустить скрипт, добавив в код эти две строки:

Sys.setenv(LANG='en') #language of my GUI, just in case
Sys.setenv(PATH='/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin')

но это ничего не изменило.


person plannapus    schedule 12.09.2014    source источник
comment
Пробовали ли вы использовать другие значения кодировки в теге ?xml вашего LaunchAgent? Или просто пытались удалить encoding="UTF-8"? Скорее всего, он взаимодействует с XML-пакетом.   -  person flodel    schedule 12.09.2014
comment
У htmlParse также есть аргумент кодировки, я бы попробовал поиграть с ним.   -  person flodel    schedule 12.09.2014
comment
Учитывая, насколько подробным был ваш пост, я почувствовал себя обязанным попытаться продублировать его на своем Mac (OS X Mavericks, R 3.1.1). Я продублировал все, и слово launchd wordcloud в порядке. Попробуйте добавить writeLines(capture.output(Sys.getenv()), con="/tmp/launchenv.txt") в сценарий и посмотрите, есть ли существенные различия в переменных среды между вашим интерактивным сеансом и тем, что настраивается с помощью launchd.   -  person hrbrmstr    schedule 12.09.2014
comment
@flodel Я избавился от части encoding в файле plist, но получил тот же результат.   -  person plannapus    schedule 12.09.2014
comment
@flodel Я пытался добавить encoding="UTF-8" к htmlParse безрезультатно. Но документ htmlParse во всяком случае говорит, что If the XML/HTML document does specify its own encoding that value is used regardless of any value specified by the caller. и этот rss-канал также начинается с <?xml version='1.0' encoding='UTF-8'?>.   -  person plannapus    schedule 12.09.2014


Ответы (1)


Я думаю, что наконец решил эту проблему, столкнувшись с ней в другой ситуации.

Рассмотрим этот код, аналогичный коду в вопросе, с той разницей, что вывод представляет собой текстовый файл:

#!/usr/bin/Rscript
require(wordcloud)
require(tm)
require(XML)
titles <- xpathSApply(htmlParse("http://www.lemonde.fr/rss/une.xml"),"//item/title",xmlValue)
titles <- gsub("[[:punct:]]"," ",titles)
rss <- Corpus(VectorSource(titles),readerControl=list(language="fr"))
rss <- tm_map(rss, stripWhitespace)
rss <- tm_map(rss, function(x)removeWords(x,stopwords("fr")))
tdm <- TermDocumentMatrix(rss)
m <- as.matrix(tdm)
v <- sort(rowSums(m),decreasing=TRUE)
d <- data.frame(word = names(v),freq=v)
sink("test.txt")
for(i in d$word) cat(i,"\n")
sink()

Сейчас я работаю на Mac OSX 10.10, и теперь проблема, представленная в вопросе, возникает также при запуске скрипта через терминал, а не только с помощью launchd. Результирующий файл test.txt в обоих случаях содержит:

contre 
crise 
des 
2015 
2<U+00A0>milliards 
<U+00A0>centre 
<U+00AB><U+00A0>il 
<U+00AB><U+00A0>jungle<U+00A0><U+00BB> 
<U+00E9>limin<U+00E9>s 
<U+00E9>lus 
<U+2019>attaque 
<U+2019>etat 
<U+2019>europe 
<U+2019>euros 
<U+2019>opposition 
<U+2019>union 
acc<U+00E9>l<U+00E8>re 
...

Я считаю, что проблема не в кодировании во время ввода, а в кодировании во время вывода. Здесь sink использует кодировку сеанса по умолчанию.

> getOption("encoding")
[1] "native.enc"

Так называемый 'native.enc' предоставляется Sys.getlocale("LC_CTYPE") в соответствии с этот комментарий Брайана Рипли.

В RGUI моя кодировка по умолчанию:

> Sys.getlocale("LC_CTYPE")
[1] "en_US.UTF-8"

В то время как кодировка по умолчанию в среде Rscript:

$ Rscript -e 'Sys.getlocale("LC_CTYPE")'
[1] "C"

Отсюда следующее (хакерское) решение для кода в вопросе:

#!/usr/bin/Rscript
require(wordcloud)
require(tm)
require(XML)
Sys.setlocale("LC_CTYPE", "en_US.UTF-8") # <- Here
titles <- xpathSApply(htmlParse("http://www.lemonde.fr/rss/une.xml"),"//item/title",xmlValue)
titles <- gsub("[[:punct:]]"," ",titles)
rss <- Corpus(VectorSource(titles),readerControl=list(language="fr"))
rss <- tm_map(rss, stripWhitespace)
rss <- tm_map(rss, function(x)removeWords(x,stopwords("fr")))
tdm <- TermDocumentMatrix(rss)
m <- as.matrix(tdm)
v <- sort(rowSums(m),decreasing=TRUE)
d <- data.frame(word = names(v),freq=v)
png("/path/to/wordcloud.png",w=5,h=5,units="in",res=100)
par(mar=c(0,0,0,0))
wordcloud(d$word,d$freq,scale=c(3,.1),min.freq=2)
dev.off()
person plannapus    schedule 26.02.2016