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

前端 未结 7 1392
攒了一身酷
攒了一身酷 2020-12-29 20:53

I would like to write a function :

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

that returns the file separator of the system in standar

相关标签:
7条回答
  • 2020-12-29 20:57

    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
    
    0 讨论(0)
  • 2020-12-29 21:01

    that can be something like this

    #if defined(WIN32) || defined(_WIN32) 
    #define PATH_SEPARATOR "\\" 
    #else 
    #define PATH_SEPARATOR "/" 
    #endif 
    
    0 讨论(0)
  • 2020-12-29 21:06

    std::filesystem::path::preferred_separator should now be used as a clean C++17 library function how it seems.

    0 讨论(0)
  • 2020-12-29 21:09

    If your compiler already offers c++17 capabilities, then you can use std::experimental::filesystem::path::preferred_separator which should return either / or \ depending on your platform.

    See this for more information.

    0 讨论(0)
  • 2020-12-29 21:11

    I'm surprised no one has offered the following. This builds a bit on what others are offering here.

    Although In this example I'm trying to dynamically grab the name of the executable being run for usage, it wouldn't be too hard to make the jump and reapply this however you need.

    Windows uses forward slash to denote arguments. So you could check for that first in the first argument argv[0], which contains the name of the program being run.

    Note the following results in stripping the pathname previous of the last slash, leaving sepd as the filename of the program.

    #include <string.h>
    #include <stdio.h>
    
    int main(int argc, char *argv[]){
    //int a = 1
    //int this = (a == 1) ? 20 : 30;  //ternary operator
    //is a==1 ? If yes then 'this' = 20, or else 'this' = 30
        char *sepd = (strrchr(argv[0], '\/') != NULL) ? 
            strrchr(argv[0], '\/') : 
            strrchr(argv[0], '\\');
        printf("%s\n\n", sepd);
        printf("usage: .%s <host> \n\n", sepd);
        while (getchar() != '\n');
    }
    

    But in all reality, this is pretty dirty and with Windows' most recent move to include Bash (not yet implemented at this time), this may produce unexpected or unanticipated results.

    It's also not as sane and impervious to errors as what others have offered, particularly #ifdef _WIN32.

    0 讨论(0)
  • 2020-12-29 21:15

    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.

    0 讨论(0)
提交回复
热议问题