26

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?

2
  • 4
    I think it's just to cover the possibility that things may be weird on non-POSIX, rather than a specific thing, especially if native and generic format are different. Commented May 31, 2025 at 10:09
  • 3
    Consider OpenVMS (a non-POSIX environment) paths where the file name includes a version – device:[dir.subdir.subdir]filename.ext;version, e.g. DSA3:[USERS.GRAWITY.SSH2]ID_RSA.PUB;1. Unfortunately the OpenVMS system I have access to doesn't seem to support <filesystem>, so I can only guess at what the output of Homer512's test program would be, but I expect that you'd get filename="ID_RSA.PUB;1" but stem="ID_RSA" and extension=".PUB". Commented Jun 2, 2025 at 5:23

2 Answers 2

31

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.

Sign up to request clarification or add additional context in comments.

4 Comments

What does p.filename() return? Does the filename contain the stream? What does p.stem() + p.extension() != p.filename() return in this case?
@jabaa good point. I added the relevant output. It contains the stream identifier
According to the cppreference, the output baz is wrong. It should be .baz. The extension should contain the dot. Did you actually run your code? How could it be, that the output contains a typo?
@jabaa Thanks! I missed that when copying the line
3

I have seen a file system where a JPG file named photo would have a filename of photo[JPG]. Ages ago. But on POSIX, if you are careless you might produce photoJPG or photo..JPG instead of photo.JPG. I'd look for a library function that is guaranteed to always return the correct result instead of relying on string concatenation.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.