33

In an example:

var assets = "images/"

var sounds = assets+"sounds/"

Is it more conventional to put the slash on the back of a file path?

var assets = "/images"

var sounds = assets+"/sounds"

Is there another method that is a good common practice?

  • Java has the static Strings File.separator and File.pathSeparator which sounds relevant. This way you are safe across all platforms – Evorlor Jan 2 '15 at 17:50
  • 1
    @Evorlor You rarely need to use File.separator though, the File and Path APIs accept both / and `\` . – kapex Jan 2 '15 at 18:28
  • 2
    Could you indicate which language you are using, please? It’s probably worth adding the corresponding tag. – Christopher Creutzig Jan 2 '15 at 23:21
  • @ChristopherCreutzig I'm using Java - although I was asking if there were any commonly used conventions for combining file directories in strings. Apparently there are a few generally accepted rules and some common sense is involved, but it varies a bit from language to language. – iiridescent Jan 3 '15 at 6:42
  • 1
    For what it's worth, in the unix world (and in urls), multiple forward slashes in the middle of a path are treated identically to a single one, so nothing bad would happen if you err on the side of more slashes. It's part of the Single Unix Specification; see this answer - unix.stackexchange.com/a/1919/21161 – yoniLavi Jan 8 '15 at 17:38
37

Nearly every major programming language has a library to handle the directory separators for you. You should leverage them. This will simplify your code and prevent bugs.

In my experience, the usual reason for combining strings like this is that they come from different sources. Sometimes it's different pieces from a configuration file. Sometimes it's a constant combining with a function argument. In any and all cases, when they come from different sources, you have to consider several different possible cases regarding the separators on the ends to be combined:

  • Both ends could have a separator: "images/" and "/sounds"
  • Only one has a separator: "images" and "/sounds" or "images/" and "sounds"
  • Neither has a separator: "images" and "sounds"

The fact each part comes from a different source means each source might have its own ideas about what conventions to follow, if someone gave any thought to it at all! Whatever is calling your code should not have to worry about this. Your code should handle all cases because someone will violate your convention. This will result in wasted time investigating the cause of an error and making a fix. I have had several unpleasant occasions where a coworker made an assumption about how paths should be formatted in a configuration file, meaning I had to go hunt down the code and figure out what they were expecting (or fix the code).

Most major languages provide a method to do this for you that already handles many of the cases:

There is a caveat with these. A number of these seem to assume that a leading directory separator in the second argument refers to a root path and that this means the first argument should be dropped entirely. I don't know why this is considered useful; for me, it just causes problems. I've never wanted to combine two path portions and end up with the first part being dropped. Read the documentation carefully for special cases, and if necessary, write a wrapper that does what you want with these instead of their special handling.

This additionally helps if you have any need for supporting different operating systems. These classes almost ubiquitously account for choosing the correct separator. The libraries usually have a way of normalizing paths to fit the OS conventions, as well.

In the event that your programming language does not have a readily available library, you should write a method that handles all these cases and use it liberally and across projects.

This falls into the category of "don't make assumptions" and "use tools that help you."

  • 2
    .NET's Path.Combine is not broken. Just don't feed it separators. make sure that you read the documentation, if the second argument is a root path it has a defined outcome. You might not like it but that doesn't mean it is broken. – Erno Jan 3 '15 at 7:35
  • 4
    Make sure you read the documentation to ensure it's not trying to be too clever. I once used a library that could successfully combine C:\Documents and Settings\Admin with my folder:document.txt on a *nix system to produce /home/admin/my folder/document.txt -- a cute trick, but in the real world, the heuristics involved introduced more bugs than they fixed. – Mark Jan 4 '15 at 1:08
  • 1
    Also, for Java, Paths.get() just converts a single String into a Path object. To join paths, you'd use Path.resolve(), which can take in another Path or a String. There's other methods in the Path class that further allow for joining paths in various ways. – Kat Jan 6 '15 at 22:06
  • 1
    My bad, it seems I didn't read the docs for Paths very well. – Kat Jan 7 '15 at 0:47
  • 1
    On PowerShell, an alternative to the .NET method [System.IO.Path]::Combine("abc", "\def") which has the described behavior, is the cmdlet Join-Path "abc" "\def" which gives "abc\def". – Jeppe Stig Nielsen May 5 '18 at 6:53
37

In Java, the answer would be "neither of the above". Best practice would be to assemble pathnames using the java.io.File class; e.g.

File assets = new File("images");
File sounds = new File(assets, "sounds");

The File class also takes care of platform-specific pathname separators.

There is a separate issue of whether your pathname should start with a slash or not. But that is more to do with correctness than best practice. A pathname that starts with a slash means something different to a pathname that doesn't!!


There isn't explicit support for pathname handling in the core (ECMA) Javascript library, but (at least) Node.js provides support via the Path module.

  • 4
    Something similar is also the case for the .Net Framework languages and any others that offer filesystem classes. – James Snell Jan 2 '15 at 10:50
  • 3
    Thank you! This seemed like the most helpful answer, even though language specific, libraries should exist for other languages in general, like .NET and C++; – iiridescent Jan 2 '15 at 15:16
  • 3
    Really, any code that does not use a library should be rejected in code review. In the rare chance no library exists, the answer would be to write one yourself rather than pasting raw strings. – Steven Burnap Jan 2 '15 at 17:10
  • C++ has Boost::Filesystem, and C# has System.IO.Path – Mooing Duck Jan 2 '15 at 21:08
  • Python has os.path.join. PowerShell has join-path. I would add a something to this answer. I have found that if you need file paths in multiple pieces, it makes your code very fragile if you make assumptions about any of them having file paths in particular places. Using these classes not only helps with portability, but it also handles all the possible edge cases (slash on both ends to be joined, slash on only one side, no slash between at all). This flexibility is invaluable when you're dropping file paths in a configuration file. – jpmc26 Jan 2 '15 at 23:37
21

Note that in .NET you should use the Path.Combine method.

var path = System.IO.Path.Combine("assets", "sounds");

The reason for this is that it 'knows' the correct characters to be used when constructing the folder names.

This takes away the 'problem' of pre or post fixing.

  • 4
    os.path.join does basically the same thing for python, too – StarWeaver Jan 2 '15 at 15:39
  • Note that path.combine does not get you out of the business of worrying about the seperator: stackoverflow.com/questions/53102/… – jmoreno Jan 2 '15 at 16:31
  • 1
    @jmoreno - In my example there are NO separators. The question you linked to has hard coded separators and if fundamentally wrong because the second path is an absolute path. – Erno Jan 2 '15 at 16:49
  • Be careful with this, though. I'm unsure about .NET, but os.path.join('src', '../../../your_secret_stuff') is valid in Python; in other words, don't blindly use these methods on user input. – sapi Jan 2 '15 at 23:34
  • @sapi - Of course, user input should always be sanitized but that is the responsibility of the programmer, not of the API. – Erno Jan 3 '15 at 7:32
5

When building paths I often use a function that adds the trailing slash if it isn't already there. Then paths can be built like:

filename := fs( 'assets') + fs( 'images') + fs( 'icons') + 'some.png';

where fs() adds a trailing slash if its needed.

5

Folders and files differ only in one aspect: folders end with a slash where files do not. Furthermore, absolute paths begin with a / where relative paths do not. If you use this consistently concatenating paths and files together should be no problem.

var absolutepath = "/my/path/";
var relativepath = "css/";
var filename = "test.css";
var relativepathtofilename = "js/test.js";

var a = absolutepath + relativepath + filename; //Output: /my/path/css/test.css
var b = absolutepath + relativepathtofilename;  //Output: /my/path/js/test.js

Concatenating two absolute paths together makes no sense, since the second path should be relative to the first path. Concatenating two relative paths together is no problem, but might lead to undefined behavior if the program does not know where the relative path is relative to.

  • This probably answered my original question best, I think I understand file paths better, though like Stephen C and Erno said, language libraries are a best first bet. This explains convention better though. Thank you! – iiridescent Jan 2 '15 at 18:40
  • File system paths or URLs? – MrWhite Jan 3 '15 at 19:22
  • 1
    For all intents and purposes, you can apply this on uri's as well. An absolute uri would start with a protocol, but besides that this would be the same I think. – Sumurai8 Jan 3 '15 at 23:18
  • Not sure how your output is working. When I do it I get: var a = "/my/path" + "css/" + "test.css"; //Output: "/my/pathcss/test.css" – Damon Jun 13 '16 at 14:46
  • 1
    @Damon I made an edit. absolutepath should have ended with a slash, because it is a path. Somehow I overlooked that when I wrote this. – Sumurai8 Jun 13 '16 at 15:59
4

I think there is no magic or "common practice" on how to implement paths, but certainly string concatenation is not the way to go. You can develop your own API for dealing with cases, but it may require some effort. In particular, you should be careful about different platforms. For example, in Windows \ is the separator while in Unix-based systems / is the separator.

I'm not familiar with Javascript libraries, but I'm sure there should be libraries for handling these cases. In Java, for example, you can use the Path API to deal with platform independent path operations.

  • 3
    Windows actually does support / as path filename delimiter. This does need quirks in the command line, but file I/O APIs work nicely with forward slash. – Ruslan Jan 2 '15 at 12:57
  • en.wikipedia.org/wiki/… "the Windows system API accepts slash, and thus all the above Unix examples should work. But many applications on Windows interpret a slash for other purposes or treat it as an invalid character, and thus require you to enter backslash — notably the cmd.exe shell (often called the "terminal" as it typically runs in a terminal window)." – Mooing Duck Jan 2 '15 at 21:12
0

My personal preference is this:

var assets = "/images"

var sounds = assets+"/sounds"

I always use absolute paths (/images/...), it feels less prone to error, to me. It is also more fool proof to use var sounds = assets+"/sounds" because even if assets had a trailing slash and you ended up with /images//sounds, it would still resolve to /images/sounds. The one disclaimer being that it depends on your request handler. Apache seems to handle it fine (at least certain versions/configurations, see http://www.amazon.com//gp//site-directory//ref=nav_sad). With the other way you'd end up with /imagessounds, not so fool proof :) There is also the option of checking for double slashes and cleaning them up. Not an option with the other approach.

  • 11
    In all contexts that I know of, a path that starts with a slash (/) is an absolute path, not a relative path. Or did you mean it only for path-sections other than the first one? – Bart van Ingen Schenau Jan 2 '15 at 9:08
  • @BartvanIngenSchenau I totally agree with you and I've been calling them that for years, but every time I read an article written by a front end developer they refer to them as relative paths. I didn't want to make assumptions so I guess I picked the lesser of two evils...? Now that I know I have some folks on my side I'll update my answer :) – rpaskett Jan 2 '15 at 16:31
  • 2
    For web developers, /somewhere is a relative path because it doesn't include the host, so the browser will look it up based on the current page's host... In the web world, http://here/somewhere is an absolute URI, and /somewhereelse is relative to that. In the file-system world, /somewhere is absolute, coming from root /, and "somewhereelse" is relative to the current working directory. – Rob Jan 2 '15 at 16:57
  • 3
    @RobY, rpaskett: Going by RFC3986(the RFC that defines URIs), http://here/somewhere is an URI with an absolute path, /somewhere is a relative reference with an absolute path and somewhere/else is a relative reference with a relative path. Apparently, in those circles "relative path" is used to refer to a relative reference. – Bart van Ingen Schenau Jan 2 '15 at 17:05
  • 1
    @BartvanIngenSchenau: in windows, a path that starts with a slash is a relative path, and is relative to CWD. en.wikipedia.org/wiki/… – Mooing Duck Jan 2 '15 at 21:16
0

In Smalltalk it is straightforward to define the / method in String so that it works like this:

'assets' / 'sounds' => 'assets/sounds'.
'assets/' / 'sounds' => 'assets/sounds'.
'assets' / '/sounds' => 'assets/sounds'.
'assets/' / '/sounds' => 'assets/sounds'.

Here is a simple implementation of the method (you can make it better):

/ aString
    | slash first second |
    slash := Directory separator.
    first := self.
    (first endsWith: slash) ifTrue: [first := first allButLast].
    second := aString.
    (second beginsWith: slash) ifTrue: [second := second allButFirst].
    ^first , slash , second

Note: you might also want to pay better attention to border cases such as '' / '', 'x/' / '', etc., in order to determine the proper behavior.

protected by gnat Jan 3 '15 at 10:46

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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