100

Is there an existing function to concatenate paths?

I know it is not that difficult to implement, but still... besides taking care of trailing / (or \) I would need to take care of proper OS path format detection (i.e. whether we write C:\dir\file or /dir/file).

As I said, I believe I know how to implement it; the question is: should I do it? Does the functionality already exist in existing R package?

2 Answers 2

158

Yes, file.path()

R> file.path("usr", "local", "lib")
[1] "usr/local/lib"
R> 

There is also the equally useful system.file() for files in a package:

R> system.file("extdata", "date_time_zonespec.csv", package="RcppBDT")
[1] "/usr/local/lib/R/site-library/RcppBDT/extdata/date_time_zonespec.csv"
R> 

which will get the file extdata/date_time_zonespec.csv irrespective of

  1. where the package is installed, and
  2. the OS

which is very handy. Lastly, there is also

R> .Platform$file.sep
[1] "/"
R> 

if you insist on doing it manually.

5
  • 5
    Great answer. But, the function does not take care of trailing "/", so file.path("/home/user/","project") results in invalid /home/user//project. Is there another function, or should I do it myself (trivially, though)? Commented Oct 28, 2012 at 15:38
  • You get it if you start with an empty string: file.path("", "home", "user", "project") produces "/home/user/project" Commented Oct 28, 2012 at 15:41
  • 6
    Note that /home/user//project and /home/user/project are both valid on Unix. What OS are you on?
    – flodel
    Commented Oct 28, 2012 at 15:41
  • @flodel Er... Linux. But I didn't know about it. Thanks! And it seems it is valid on Windows (on cmd.exe) too! Commented Oct 28, 2012 at 15:42
  • 2
    This might be basic R knowledge but if you have yourlist=list("", "a", "b", "c") already and want to convert that to a path, do.call(file.path, yourlist)
    – Colin D
    Commented Feb 23, 2017 at 15:30
5

In case anyone wants, this is my own function path.cat. Its functionality is comparable with Python's os.path.join with the extra sugar, that it interprets the ...

With this function, you can construct paths hierarchically, but unlike the file.path, you leave the user the ability to override the hierarchy by putting an absolute path. And as an added sugar, he can put the ".." wherever he likes in the path, with obvious meaning.

e.g.

  • path.cat("/home/user1","project/data","../data2") yelds /home/user1/project/data2

  • path.cat("/home/user1","project/data","/home/user2/data") yelds /home/user2/data

The function works only with slashes as path separator, which is fine, since R transparently translates them to backslashes on Windows machine.

library("iterators") # After writing this function I've learned, that iterators are very inefficient in R.
library("itertools")

#High-level function that inteligentely concatenates paths given in arguments
#The user interface is the same as for file.path, with the exception that it understands the path ".."
#and it can identify relative and absolute paths.
#Absolute paths starts comply with "^\/" or "^\d:\/" regexp.
#The concatenation starts from the last absolute path in arguments, or the first, if no absolute paths are given.
path.cat<-function(...)
{
  elems<-list(...)
  elems<-as.character(elems)
  elems<-elems[elems!='' && !is.null(elems)]
  relems<-rev(elems)
  starts<-grep('^[/\\]',relems)[1]
  if (!is.na(starts) && !is.null(starts))
  {
    relems<-relems[1:starts]
  }
  starts<-grep(':',relems,fixed=TRUE)
  if (length(starts)==0){
    starts=length(elems)-length(relems)+1
  }else{
    starts=length(elems)-starts[[1]]+1}
  elems<-elems[starts:length(elems)]
  path<-do.call(file.path,as.list(elems))
  elems<-strsplit(path,'[/\\]',FALSE)[[1]]
  it<-ihasNext(iter(elems))
  out<-rep(NA,length(elems))
  i<-1
  while(hasNext(it))
  {
    item<-nextElem(it)
    if(item=='..')
    {
      i<-i-1
    } else if (item=='' & i!=1) {
      #nothing
    } else   {
      out[i]<-item
      i<-i+1
    }
  }
  do.call(file.path,as.list(out[1:i-1]))
}
1
  • 2
    Cool comment, but there is an important difference: normalizePath() requires the path to be actually present on the R server. This may not always be desired. For me it is an important difference. OTOH manual says normalizePath() converts short names to long on Windows. Commented Jan 10, 2016 at 17:49

Not the answer you're looking for? Browse other questions tagged or ask your own question.