I want to safely test if a file (e.g., called test.docx
) is open using R
.
This answer proposes a solution but the associated comments complain that it corrupts the file. I too find it corrupts the file and results in a blank docx
when I use it on test.docx
.
Code to produce an example docx
file using rmarkdown
(called test.Rmd
):
---
title: "test"
output:
officedown::rdocx_document:
---
# heading 1
# heading 2
render call:
library(bookdown)
library(officedown)
library(officer)
rmarkdown::render('test.Rmd',
output_format = 'rdocx_document')
Using the proposed solution to test if open:
file.opened <- function(path) {
suppressWarnings(
"try-error" %in% class(
try(file(path,
open = "w"),
silent = TRUE
)
)
)
}
file.opened("test.docx")
# [1] FALSE
It correctly returns FALSE
as I have not opened the file yet, but the file has now been edited and is blank (size of file reduced to 0
).
What is a safe way to test if it is open or not?
Thanks
As Hadley Wickham writes in Advanced R:
It is possible, but not recommended, to save the result of
try()
and perform different actions based on whether or not the code succeeded or failed. Instead, it is better to usetryCatch()
.
This is how I would write this function:
is_file_open <- function(path, mode = "r+b") {
tryCatch(
{
con <- suppressWarnings(file(path, open = mode))
on.exit(close(con))
FALSE
},
error = function(e) TRUE
)
}
We can open the file with mode "r+b"
. From the docs for file()
:
"r+"
"r+b"
: Open for reading and writing.
As docx is binary we should use b
though I don't think it matters if you're not writing to the file. This gives the correct output and does not delete the contents of the file.
Furthermore, with your previous function, if you run it a few times and then run showConnections()
, it will print something like Warning message: In .Internal(gc(verbose, reset, full)) :closing unused connection 4 (test.docx)
. We can avoid that by explicitly closing the connection when we exit the function.
# 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
A problem with this is_file_open()
approach, however, is that it returns TRUE
if it encounters any error. This may not be an issue in this case, but you can't actually use the function to reliably test if a file is open.
I think it would be better to have a function like this:
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
)
}
This does what a user would expect from the function name.