Most programming languages have built-in support for manipulating path. For example, Node.js has path module, C# has Path class. But the semantics of them are not necessary the same, sometimes causing confusion for programmers used to one certain programming language.

Node.js path.join() vs. C# Path.Combine() on Windows

Node.js path.join(): Backslash after drive specifier is optional.

> path.win32.join('C:', 'file.txt')
'C:\\file.txt'
> path.win32.join('C:\\', 'file.txt')
'C:\\file.txt'

C# Path.Combine(): Backslash after drive specifier is required.

> Path.Combine("C:", "file.txt")
"C:file.txt"
> Path.Combine("C:\\", "file.txt")
"C:\\file.txt"

Node.js path.join(): The second and following parameters (counting from 1) are always relative.

> path.win32.join('C:\\x', 'y')
'C:\\x\\y'
> path.win32.join('C:\\x', '\\y')
'C:\\x\\y'
> path.win32.join('C:\\x', 'y', '\\z')
'C:\\x\\y\\z'

C# Path.Combine(): All parameters can be absolute or relative.

> Path.Combine("C:\\x", "y")
"C:\\x\\y"
> Path.Combine("C:\\x", "\\y")
"\\y"
> Path.Combine("C:\\x", "y", "\\z")
"\\z"

Node.js path.join(): Resulting path is normalized.

> path.win32.join('a', '.\\b')
'a\\b'
> path.win32.join('a', '..\\b')
'b'
> path.win32.join('a', '..\\..\\b')
'..\\b'

C# Path.Combine(): Resulting path is not normalized.

> Path.Combine("a", ".\\b")
"a\\.\\b"
> Path.Combine("a", "..\\b")
"a\\..\\b"
> Path.Combine("a", "..\\..\\b")
"a\\..\\..\\b"

Node.js path.join(): Forward slash and backslash are all normalized to backslash.

> path.win32.join('a/b', 'c\\d')
'a\\b\\c\\d'

C# Path.Combine(): Forward slash and backslash are left as is.

> Path.Combine("a/b", "c\\d")
"a/b\\c\\\d"

Node.js path.join(): Zero-length resulting path is returned as . (representing the current working directory).

> path.win32.join('', '')
'.'

C# Path.Combine(): Zero-length resulting path is returned as is.

> Path.Combine("", "")
""