Переключение записи из файла в стандартный вывод с помощью функции приемника() в R

Я реализовал следующую процедуру, целью которой является запись некоторых файлов и печать сообщения в конце каждого файла, когда запись завершена:

# Print one file per piaf
output_dir_piafs <- "OUTPUT_dataset_piafs"
unlink(output_dir_piafs, recursive = TRUE, force = TRUE)
dir.create(output_dir_piafs)
for (i in 1:length(lst_sorted)) {
    sink()      # Generates warnings...
    filename <- paste(output_dir_piafs, "/piaf_", i, ".txt", sep="")
    sink(file = filename, append = TRUE)
    sink(type = "message")
    cat("  ", colnames(file1), "\n")

    for (j in 1:length(lst_sorted[[i]])) {
        cat(j, " ")
        lapply( lst_sorted[[i]][[j]], 
                function(x) { 
                    cat(as.character(x), " ")
                }
        )
        cat("\n")
    }

    ## back to the console
    sink()
    cat(paste(filename, "done !\n"))
    #flush(stdout())        # Tested, no particular effect
}

Моя проблема в том, что если я не добавлю sink() в самом начале цикла, окончательная запись на стандартный вывод (cat(paste(filename, "done !\n"))) не будет иметь никакого эффекта. С другой стороны, добавление этого раннего sink() генерирует предупреждения, которых я хотел бы избежать:

There were 50 or more warnings (use warnings() to see the first 50)
> warnings()
Warning messages:
1: In sink() : no sink to remove
2: In sink() : no sink to remove
3: In sink() : no sink to remove

Есть ли у кого-нибудь идеи о том, как ведет себя функция sink() и/или как избавиться от этих предупреждений?

Примечание. Я также пробовал try(sink(), silent=TRUE), но вариант silent предотвращает только ошибки...


person Gauthier Boaglio    schedule 07.11.2013    source источник
comment
Просто глупая мысль. Вы пробовали свой код из нового сеанса R? Возможно у вас открыты какие-то соединения, попробуйте sink.number("message").   -  person Roman Luštrik    schedule 07.11.2013
comment
Хорошая мысль! sink.number("message") ничего не возвращает, но из нового нового сеанса стандартный вывод записывается нормально. Только когда я останавливаю процесс (Ctrl-C), а затем перезапускаю его, стандартный вывод не работает. Итак, я думаю, вы правы. Мое плохое: прерывание процесса может иметь плохие последствия для соединения stdout. Более того, если я наберу sink() прямо перед повторным запуском процесса, он все равно не сработает...   -  person Gauthier Boaglio    schedule 07.11.2013
comment
Я сделал несколько других тестов. Если я позволю процессу завершиться правильно, а затем перезапущу его, все будет хорошо. Это определенно прерывание Ctrl-C, которое вызывает проблему. Любое предложение о том, как сделать все правильно, после того, как прервали таким образом?   -  person Gauthier Boaglio    schedule 07.11.2013
comment
Был там, сделал это, купил футболку, она не понравилась. Я рад, что ты решил свою проблему.   -  person Roman Luštrik    schedule 07.11.2013


Ответы (2)


Вам не нужно использовать sink, взгляните на ?cat, который может напрямую записывать в файл.

Следующий код должен работать:

output_dir_piafs <- "OUTPUT_dataset_piafs"
unlink(output_dir_piafs, recursive = TRUE, force = TRUE)
dir.create(output_dir_piafs)
lst_sorted <- c(1,2,3)
file1 <- c(a=1, b=2, c=3)
for (i in 1:length(lst_sorted)) {
  filename <- paste(output_dir_piafs, "/piaf_", i, ".txt", sep="")
  cat("  ", colnames(file1), "\n", file=filename, append=T)
  for (j in 1:length(lst_sorted[[i]])) {
    cat(j, " ", file=filename, append=T)
    lapply( lst_sorted[[i]][[j]], 
           function(x) { 
             cat(as.character(x), " ", file=filename, append=T)
           }
           )
    cat("\n", file=filename, append=T)
  }
  cat(paste(filename, "done !\n"))
}

Обратите внимание, что я установил переменные lst_sorted и file1, чтобы сделать код воспроизводимым.

Подходит ли вам решение cat?

person user1981275    schedule 07.11.2013
comment
Да, это работает, но для записи данных в файлы требуется вдвое больше времени, чем sink. Что довольно раздражает... Я полагаю, что это приводит к открытию/закрытию соединения с файлом при каждом cat. - person Gauthier Boaglio; 07.11.2013
comment
что касается скорости, просто создайте f ‹- file(filename, 'w') перед вашим циклом, затем вызовите cat() с file=f - person Karl Forner; 07.11.2013
comment
@KarlForner Хорошо, спасибо! Это может сделать решение cat user1981275 жизнеспособным. Посмотрим, если с прерыванием Ctrl-C нам все еще нужно закрыть файл в tryCatch... - person Gauthier Boaglio; 07.11.2013
comment
@ user1981275 Спасибо за помощь! Просто замечание: в файле также должно быть написано cat("\n"): cat("\n", file=filename, append=T). - person Gauthier Boaglio; 08.11.2013

Я, наконец, исправил проблему, добавив sink(type="output") при обнаружении прерывания - Ctrl-C - (это делает все правильно для последующего использования стандартного вывода, который в противном случае оставался бы заблокированным/отклонением):

tryCatch({

    # Print one file per piaf
    output_dir_piafs <- "OUTPUT_dataset_piafs"
    unlink(output_dir_piafs, recursive = TRUE, force = TRUE)
    dir.create(output_dir_piafs)
    for (i in 1:length(lst_sorted)) {
        filename <- paste(output_dir_piafs, "/piaf_", i, ".txt", sep="")
        sink(file = filename, append = TRUE)
        sink(type = "message")
        cat("  ", colnames(file1), "\n")

        for (j in 1:length(lst_sorted[[i]])) {
            cat(j, " ")
            lapply( lst_sorted[[i]][[j]], 
                    function(x) { 
                        cat(as.character(x), " ")
                    }
            )
            cat("\n")
        }

        ## back to the console
        sink(type="output")
        cat(paste(filename, "done !\n"))
    }

}, interrupt = function(ex) {

    ##cat("An interrupt was detected.\n")
    sink(type="output")            # Restore the standard output !
    ##print(ex)

}) # tryCatch()


Другой способ сделать это (на основе помощи user1981275 и Карл Форнер), было бы:

tryCatch({

    # Print one file per piaf
    output_dir_piafs <- "OUTPUT_dataset_piafs_2"
    unlink(output_dir_piafs, recursive = TRUE, force = TRUE)
    dir.create(output_dir_piafs)
    for (i in 1:length(lst_sorted)) {
        filename <- paste(output_dir_piafs, "/piaf_", i, ".txt", sep="")
        f <- file(filename, 'w')
        cat("  ", colnames(file1), "\n", file=f)

        for (j in 1:length(lst_sorted[[i]])) {
            cat(j, " ", file=f)
            lapply( lst_sorted[[i]][[j]], 
                    function(x) { 
                        cat(as.character(x), " ", file=f)
                    }
            )
            cat("\n", file=f)
        }

        flush(f)
        close(f)
        ## back to the console
        cat(paste(filename, "done !\n"))
    }

}, interrupt = function(ex) {

    closeAllConnections()

}) # tryCatch()

Это решение имеет большое преимущество, заключающееся в том, что стандартный выход не переключается, а это означает, что даже без обнаружения прерывания стандартный выход остается активным.

Обратите внимание, что обработка прерывания клавиатуры по-прежнему необходима, чтобы избежать предупреждений, подобных:

> warnings()
Warning message:
closing unused connection 3 (OUTPUT_dataset_piafs_2/piaf_16.txt)

Соответственно ?closeAllConnections:

«closeAllConnections» закрывает (и уничтожает) все пользовательские соединения, восстанавливая при этом все «стоковые» отклонения.

Последнее примечание. Между этими двумя методами нет реальной разницы в скорости записи файлов.

person Gauthier Boaglio    schedule 07.11.2013