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?
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
.