c++std-filesystem

When is p.stem() + p.extension() != p.filename()?


In the std::filesystem::path::extension() page on cppreference.com, it says the following:

On a non-POSIX system, it is possible that p.stem() + p.extension() != p.filename() even though generic-format versions are the same.

When could this occur?


Solution

  • NTFS supports alternative data streams. The stream selection goes after the extension, such as file.extension:stream:$DATA. This is part of the file path (to be opened) but not stem() or extension(). It is however considered filename()

    MSVC will remove these parts when returning stem or extension. Curiously, replace_extension() will remove the stream, as does replace_filename. It seems like even Microsoft only half-cares about these. Their documentation doesn't even mention it.

    #include <iostream>
    #include <filesystem>
    
    int main()
    {
        namespace fs = std::filesystem;
        fs::path p { "C:/foo/bar.baz:stream:$DATA"};
        std::cout << "stem\t" << p.stem() << '\n'
                  << "extension\t" << p.extension() << '\n'
                  << "filename\t" << p.filename() << '\n'
                  << "replace_extension\t" << p.replace_extension("quz") << '\n';
    
    }
    

    Prints

    stem              "bar"
    extension         ".baz"
    filename          "bar.baz:stream:$DATA"
    replace_extension "C:/foo/bar.quz"
    

    Other filesystems have similar features, most notably any of Apple's recent filesystems with their resource fork. It can be addressed as file.extension/rsrc or file.extension/..namedfork/rsrc. See for example Does APFS actually support Named Forks or just Resource Forks and Extended Attributes? I have no idea if and how the first syntax can even be recognized without accessing the filesystem.

    Similarly, OpenVMS supports a file version suffix, as noted by @grawity. In its nomenclature foo.bar;1 is version ;1 of the file foo with type .bar. The specification also allows for foo.bar.1.