Безопасно проверить, открыт ли файл через R

Я хочу безопасно проверить, открыт ли файл (например, с именем test.docx) с помощью R.

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

Код для создания примера файла docx с использованием rmarkdown (называемого test.Rmd):

---
title: "test"
output: 
  officedown::rdocx_document:
---


# heading 1

# heading 2

сделать вызов:

library(bookdown)
library(officedown)
library(officer)
rmarkdown::render('test.Rmd', 
                  output_format = 'rdocx_document')

Использование предлагаемого решения для проверки открытия:

file.opened <- function(path) {
  suppressWarnings(
    "try-error" %in% class(
      try(file(path, 
               open = "w"), 
          silent = TRUE
      )
    )
  )
}
file.opened("test.docx")
# [1] FALSE

Он правильно возвращает FALSE, поскольку я еще не открывал файл, но теперь он отредактирован и пуст (размер файла уменьшен до 0).

Каков безопасный способ проверить, открыт он или нет?

Спасибо


1
91
1

Ответ:

Решено

Как пишет Хэдли Уикхем в Advanced R:

Можно, но не рекомендуется, сохранить результат try() и выполнить различные действия в зависимости от того, был ли код выполнен успешно или нет. Вместо этого лучше использовать tryCatch().

Вот как я бы написал эту функцию:

is_file_open <- function(path, mode = "r+b") {
  tryCatch(
    {
        con  <- suppressWarnings(file(path, open = mode))
        on.exit(close(con))
        FALSE
    },
    error = function(e) TRUE
  )
}

Мы можем открыть файл в режиме "r+b". Из документации для file():

"r+""r+b": Открыто для чтения и записи.

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

Более того, если вы запустите предыдущую функцию несколько раз, а затем запустите showConnections(), она напечатает что-то вроде Warning message: In .Internal(gc(verbose, reset, full)) :closing unused connection 4 (test.docx). Мы можем избежать этого, явно закрывая соединение при выходе из функции.

# With the file open in MS Word
is_file_open("test.docx")
# [1] TRUE

# With the file not open
is_file_open("test.docx")
# [1] FALSE

Лучшая функция

Однако проблема с этим is_file_open() подходом заключается в том, что он возвращает TRUE, если обнаруживает какую-либо ошибку. В данном случае это может не быть проблемой, но на самом деле вы не можете использовать эту функцию для надежной проверки того, открыт ли файл.

Я думаю, что было бы лучше иметь такую ​​функцию:

can_write_to_file <- function(path, mode = "r+b") {
    if (!file.exists(path)) {
        warning("The file does not exist. Checking if it can be created.")
        tryCatch(
            {   
                # Check if we can write to a non-existent file
                close(suppressWarnings(file(path, open = "w")))
                # If we can then delete the empty file
                unlink(path)
                return(TRUE)
            },
            error = function(e) return(FALSE)
        )
    }
  # Check whether an existing file can currently be written to
  tryCatch(
    {
        close(suppressWarnings(file(path, open = mode)))
        TRUE
    },
    error = function(e) FALSE
  )
}

Это делает то, что пользователь ожидает от имени функции.