Skip to content

Instantly share code, notes, and snippets.

@r2evans
Last active July 4, 2017 04:23
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save r2evans/755bd663f87ff75a5aaf4397973206bb to your computer and use it in GitHub Desktop.
numbered filenames, "guaranteed" to produce a unique and sequential filename
#' Sequential file naming
#'
#' (https://stackoverflow.com/a/44868545/3358272)
#'
#' @param ptn string that includes a [base::sprintf()] format string for an
#' integer, either `%d` or `%03d`; for example, `"filename-%03d.pdf"`
#' @param full.names logical, passed to [base::files()]
#' @return character, representing a filename that did not exist the
#' moment the function was called; NB: there is a potential
#' race-condition here
#' @md
#' @example
#' \dontrun{
#' ### starts at 1 for non-existent files
#' sequentialFilename("does-not-yet-exist-%d.pdf")
#' # [1] "does-not-yet-exist-1.pdf"
#' sequentialFilename("does-not-yet-exist-%05d.pdf")
#' # [1] "does-not-yet-exist-00001.pdf"
#'
#' ### empty files, please do not blame me if you over-write pre-existing files!
#' writeLines("", "fn-001")
#' writeLines("", "fn-002")
#' sequentialFilename("fn-%03d")
#' # [1] "fn-003"
#'
#' ### finds the max, not the next unused
#' writeLines("", "fn-100")
#' sequentialFilename("fn-%03d")
#' # [1] "fn-101"
#'
#' ### can extend beyond the patterned number
#' writeLines("", "fn-1000")
#' sequentialFilename("fn-%03d")
#' # [1] "fn-1001"
#' }
sequentialFilename <- function(ptn, full.names = FALSE) {
if (! nchar(gsub("[^%]", "", ptn)) == 1L ||
! grepl("%[0-9]*d", ptn))
stop("'ptn' must contain exactly one '%' sprintf formatting string")
# extract the numbering portion, to convert into a filename glob
gre <- gregexpr("%[0-9]*d", ptn)
# escape globbing characters
glob <- gsub("([.*?])", "\\\\\\1", ptn)
regmatches(glob, gre) <- "[0-9]+"
files <- list.files(pattern = glob, full.names = full.names)
# find the highest used numbered filename
highestnumber <-
if (! length(files)) {
0
} else {
prepost <- regmatches(ptn, gre, invert = TRUE)[[1L]]
max(as.integer(
mapply(substr, files,
1 + nchar(prepost[1]), nchar(files) - nchar(prepost[2]))
), 0, na.rm = TRUE)
}
if (is.na(highestnumber))
stop("something went wrong, could not find the next number")
nextfile <- sprintf(ptn, highestnumber + 1)
browser()
if (file.exists(nextfile))
stop(paste("oops, the next-numbered file already exists ...",
sQuote(nextfile)), call. = FALSE)
nextfile
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment