std::filesy...::path::stem(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’… std::filesystem::path::stem β€” the C++ Standard Library glow-up

okay so bestie, you clicked on std::filesy...::path::stem(3) from the C++ Standard Libary and honestly? iconic choice. this is the page that teaches you how to peel the extension off a filename and keep the good part. slay. πŸŽ€


✨ NAME ✨

std::filesystem::path::stem β€” literally just std::filesystem::path::stem, no cap, that's the whole name 🫢


🌸 Synopsis 🌸

path stem() const;  (since C++17)

so here's the vibe: this bad boy returns the filename identified by the generic-format path stripped of its extension. that's it. that's the whole job. πŸ’…

more specifically β€” and this is the part people always get wrong, so pay attention β€” it returns the substring from the beginning of filename() up to and not including the last period (.) character, with the following exceptions:

  • πŸ¦‹ If the first character in the filename is a period, that period is ignored (a filename like ".profile" is not treated as an extension). so dotfiles keep their dot. we respect the dotfiles here.
  • πŸ¦‹ If the filename is one of the special filesystem components dot or dot-dot, or if it has no periods, the function returns the entire filename(). no periods? no problem, you get everything back.

🧚 Parameters 🧚

(none) β€” she takes nothing. she needs nothing. main character energy fr. ✨


πŸ‘‘ Return value πŸ‘‘

The stem of the filename identified by the path (i.e. the filename without the final extension). lowkey the most useful little string you'll ever grab. πŸ”₯


πŸ’€ Exceptions πŸ’€

May throw implementation-defined exceptions. so like... it might be messy, and it's up to your implementation to tell you how. not me being vague, that's just what the page says πŸ™ˆ


🌈 Example 🌈

// Run this code

#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;

int main()
{
    for (const fs::path p : {"/foo/bar.txt", "/foo/.bar", "foo.bar.baz.tar"})
        std::cout << "path: " << p << ", stem: " << p.stem() << '\n';

    std::cout << '\n';

    for (fs::path p = "foo.bar.baz.tar"; !p.extension().empty(); p = p.stem())
        std::cout << "path: " << p << ", extension: " << p.extension() << '\n';
}

⭐ Output ⭐

path: "/foo/bar.txt", stem: "bar"
path: "/foo/.bar", stem: ".bar"
path: "foo.bar.baz.tar", stem: "foo.bar.baz"

path: "foo.bar.baz.tar", extension: ".tar"
path: "foo.bar.baz", extension: ".baz"
path: "foo.bar", extension: ".bar"

look at that second loop bestie β€” it just keeps calling stem() on itself until extension() is empty, and you get to watch the extension peel off one layer at a time. it's giving onion. it's giving satisfying. obsessed. πŸ§…βœ¨


🫢 See also 🫢

  • filename β€” returns the filename path component (public member function)
  • extension β€” returns the file extension path component (public member function)

the little trio: filename() gives you the whole name, extension() gives you the tail, and stem() gives you everything but the tail. iykyk πŸ’œ


real talk though: exceptions are implementation-defined, so don't come crying to me if your compiler has opinions. go read its docs. πŸŽ€

and that's the page, babe! brought to you by http://cppreference.com, dated 2024.06.10 β€” mwah, go write something slay πŸ’…πŸŒΈπŸ¦‹

πŸ“¦ where this page comes from