Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the file separator symbol in standard C/C++ : / or \?

I would like to write a function :

inline char separator()
{
    /* SOMETHING */
}

that returns the file separator of the system in standard C/C++/C++11 ? (I mean slash or backslash depending on the system). Is there a way to achieve this ?

like image 922
Vincent Avatar asked Oct 19 '12 09:10

Vincent


3 Answers

I'm not sure how to do it other than by checking ifdefs

inline char separator() { #ifdef _WIN32     return '\\'; #else     return '/'; #endif } 

or (as suggested by PaperBirdMaster)

const char kPathSeparator = #ifdef _WIN32                             '\\'; #else                             '/'; #endif 
like image 106
simonc Avatar answered Sep 19 '22 05:09

simonc


If your compiler offers c++17 capabilities, then you can use std::filesystem::path::preferred_separator which yields the preferred separator char depending on your platform. For example, on Windows this would usually be \ whereas on Linux you'd get /.

See this for more information.

like image 21
Overblade Avatar answered Sep 20 '22 05:09

Overblade


This question is really hinting at a much nastier problem.

If you simply care about UNIX vs. Winodws and you only care about directories and files, then what you've already seen will (mostly) work, but the more generic issue of splicing a path name into its components is a much uglier problem. Depending on the platform, a path may include one or more of:

  • Volume identifier
  • List of directories
  • File-name
  • Sub-stream within the file
  • Version number

While there are 3rd party libraries (like various CPAN Perl modules, Boost, and others) for this, and every OS includes system functions for this, there's nothing built-in to C for this and the C++ standard only gained this functionality (by incorporating the Boost module) in 2017.

Some examples of what such a function may need to deal with are:

  • UNIX and UNIX-like systems use a list of strings separated by "/" characters, with a leading "/" to indicate an absolute path (vs. a relative path). In some contexts (like NFS), there may also be a host-name prefix (with a ":" delimiter)
  • DOS and DOS-derived OS's (Windows, OS/2 and others) use "\" as a directory separator (with the APIs also accepting "/"), but paths may also be prefixed with volume information. It could be a drive letter ("C:"), or a UNC share name ("\\MYSERVER\SHARE\") There are additional prefixes to represent different kinds of servers and suffixes to represent non-default streams within a file.
  • Macs (Classic Mac OS, Carbon and some Cocoa APIs) use ":" as a directory separator, with the first term being a volume name, not a directory name. Mac files may also contain sub-streams ("forks"), which are accessed via the same name using special-purpose APIs. This is especially important for the resource fork, which is used extensively in classic Mac software.
  • Mac OS X, when using the UNIX APIs generally does what UNIX-like systems do, but they can also represent named sub-streams ("forks") by suffixing a "." followed by the fork-name to the file-name.
  • The latest versions of Cocoa (Mac OS X, iOS, etc.) recommend using a URL-based API to represent files, due to the ever-increasing complexity of this problem. Think about things like cloud-based documents and other complicated networked file systems.
  • VMS is pretty complicated (https://web.archive.org/web/20160324205714/http://www.djesys.com/vms/freevms/mentor/vms_path.html), but it has components that represent a volume, directory-path, file and file-revision.

There are many others as well.

It is worth noting that the C++17 filesystem library does not cover all of these possibilities. The std::filesystem::path consists of an optional root-name (a volume identifier), an optional root-directory (to identify absolute paths), and a sequence of filenames separated by directory separators. This covers everything likely to be valid on UNIX platforms and the majority of use-cases for other platforms, but is not comprehensive. For example, it does not have any support for sub-streams (relying on the OS to somehow map them onto a file name - which is done by Mac OS X, but not classic MacOS). It also does not include support for file version numbers.

See also Wikipedia's entry on Path and the C++17 std::filesystem::path class

http://en.cppreference.com/w/cpp/filesystem

I recommend you look at what you want to do with the directory separator (extract the base-name, break a path into a list of directories, etc.) and write a function to do that. If you're using C++17 (and you are certain your code won't be compiled by a pre-17 C++ compiler) then you can (probably) use standard C++ library code to write a portable implementation of this function. If not, that function will need to use platform-specific #ifdefs for each platform you will be supporting, using a #error if none of the conditions are met, to force you to to add conditions for unexpected platforms.

Or use a 3rd party library (like Boost) that includes functions for all of this, if that is acceptable.

like image 40
David C. Avatar answered Sep 19 '22 05:09

David C.