74

I am trying to grab the raw filename without the extension from the filename passed in arguments:

int main ( int argc, char *argv[] )
{
    // Check to make sure there is a single argument
    if ( argc != 2 )
    {
        cout<<"usage: "<< argv[0] <<" <filename>\n";
        return 1;
    }

    // Remove the extension if it was supplied from argv[1] -- pseudocode
    char* filename = removeExtension(argv[1]);

    cout << filename;

}

The filename should for example be "test" when I passed in "test.dat".

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Flarmar Brunjd
  • 753
  • 1
  • 5
  • 6

11 Answers11

106
size_t lastindex = fullname.find_last_of("."); 
string rawname = fullname.substr(0, lastindex); 

Beware of the case when there is no "." and it returns npos

martinus
  • 17,736
  • 15
  • 72
  • 92
Adithya Surampudi
  • 4,354
  • 1
  • 17
  • 17
  • 22
    Sorry to add a comment to such an old answer, but `substr`'s second parameter's default value is `npos`, so you don't have to worry if `find_last_of` returns `npos`, you can simply do: `rawname = fullname.substr(0, fullname.find_last_of("."));` – Andy Dalton Oct 17 '15 at 14:55
  • 4
    Sometimes directory could consist "." eg.: projects/Name.Surname/binary in that case code above would return: projects/Name – baziorek Mar 14 '17 at 11:47
  • 2
    Warning: In addition to Grzegorz correct comment, this ould also "fail" (not the expected result) if the filename starts with "." and has no extension. – Marco Freudenberger May 19 '17 at 10:30
  • 1
    @MarcoFreudenberger [Here](https://en.cppreference.com/w/cpp/experimental/fs/path/extension) (Example section), for `std::cout << fs::path("/foo/.hidden").extension()` prints `.hidden`, I guess in that case we can assume file has an extension. – Vladimir Gamalyan Jun 10 '18 at 20:49
  • This also does not work for multiple extensions like .tar.gz. – Phidelux Jun 04 '21 at 07:04
33

This works:

std::string remove_extension(const std::string& filename) {
    size_t lastdot = filename.find_last_of(".");
    if (lastdot == std::string::npos) return filename;
    return filename.substr(0, lastdot); 
}
orlp
  • 112,504
  • 36
  • 218
  • 315
  • Fails for "projects/Name.Surname/binary". – zett42 Jul 05 '18 at 16:13
  • @zett42 Depends on your definition of fail. The function accepts a file name, not a path. A function that would work on any path would definitely be more useful though. – orlp Jul 05 '18 at 17:03
  • Right, when taking the question literally, your code is correct. People often swap "file name" for "file path" though, even in official API documentation. – zett42 Jul 05 '18 at 17:11
31

Since C++17 you can use std::filesystem::path::replace_extension with a parameter to replace the extension or without to remove it:

#include <iostream>
#include <filesystem>
 
int main()
{
    std::filesystem::path p = "/foo/bar.txt";
    std::cout << "Was: " << p << std::endl;
    std::cout << "Now: " << p.replace_extension() << std::endl;
}

Compile it with:

g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

Running the resulting binary leaves you with:

Was: "/foo/bar.txt"
Now: "/foo/bar"

However this does only remove the last file extension:

Was: "/foo/bar.tar.gz"
Now: "/foo/bar.tar"
Phidelux
  • 2,043
  • 1
  • 32
  • 50
22

In my opinion it is easiest, and the most readable solution:

#include <boost/filesystem/convenience.hpp>

std::string removeFileExtension(const std::string& fileName)
{
    return boost::filesystem::change_extension(fileName, "").string();
}
baziorek
  • 2,502
  • 2
  • 29
  • 43
  • 7
    I think most people would disagree with you – jiggunjer Jul 13 '15 at 10:20
  • 10
    This code gives the correct result for the example path "projects/Name.Surname/binary" for which the **accepted answer fails** (and all others that are variants of `fileName.substr(0, fileName.find_last_of("."))`. See [live demo on Coliru](http://coliru.stacked-crooked.com/a/c0ab50cb9c667877). – zett42 Jul 05 '18 at 16:12
  • Is this header only? I don't think so. – stephanmg Apr 29 '20 at 15:08
17

For those who like boost:

Use boost::filesystem::path::stem. It returns the filename without the last extension. So ./myFiles/foo.bar.foobar becomes foo.bar. So when you know you are dealing with only one extension you could do the follwing:

boost::filesystem::path path("./myFiles/fileWithOneExt.myExt");
std::string fileNameWithoutExtension = path.stem().string();

When you have to deal with multiple extensions you might do the following:

boost::filesystem::path path("./myFiles/fileWithMultiExt.myExt.my2ndExt.my3rdExt");
while(!path.extension().empty())
{
    path = path.stem();
}

std::string fileNameWithoutExtensions = path.stem().string();

(taken from here: http://www.boost.org/doc/libs/1_53_0/libs/filesystem/doc/reference.html#path-decomposition found in the stem section)

BTW works with rooted paths, too.

anhoppe
  • 4,287
  • 3
  • 46
  • 58
12

The following works for a std::string:

string s = filename;
s.erase(s.find_last_of("."), string::npos);
Shiroko
  • 1,437
  • 8
  • 12
5

More complex, but with respect to special cases (for example: "foo.bar/baz", "c:foo.bar", works for Windows too)

std::string remove_extension(const std::string& path) {
    if (path == "." || path == "..")
        return path;

    size_t pos = path.find_last_of("\\/.");
    if (pos != std::string::npos && path[pos] == '.')
        return path.substr(0, pos);

    return path;
}
3

You can do this easily :

string fileName = argv[1];
string fileNameWithoutExtension = fileName.substr(0, fileName.rfind("."));

Note that this only work if there is a dot. You should test before if there is a dot, but you get the idea.

Baptiste Wicht
  • 7,472
  • 7
  • 45
  • 110
1

In case someone just wants a simple solution for windows:

Use PathCchRemoveExtension ->MSDN

... or PathRemoveExtension (deprecated!) ->MSDN

0

Try the following trick to extract the file name from path with no extension in c++ with no external libraries in c++ :

#include <iostream>
#include <string>

using std::string;

string getFileName(const string& s) {
char sep = '/';
#ifdef _WIN32
sep = '\\';
#endif
size_t i = s.rfind(sep, s.length());
if (i != string::npos) 
{
  string filename = s.substr(i+1, s.length() - i);
  size_t lastindex = filename.find_last_of("."); 
  string rawname = filename.substr(0, lastindex); 
  return(rawname);
}

return("");
}

int main(int argc, char** argv) {

string path = "/home/aymen/hello_world.cpp";
string ss = getFileName(path);
std::cout << "The file name is \"" << ss << "\"\n";
}
Aymen Alsaadi
  • 1,329
  • 1
  • 11
  • 12
-2

Just loop through the list and replace the first (or last) occurrence of a '.' with a NULL terminator. That will end the string at that point.

Or make a copy of the string up until the '.', but only if you want to return a new copy. Which could get messy since a dynamically allocated string could be a source of memory leak.

for(len=strlen(extension);len>= 0 && extension[len] != '.';len--)
     ;

char * str = malloc(len+1);

for(i=0;i<len;i++)
  str[i] = extension[i];

 str[i] = '\0'l
stands2reason
  • 672
  • 2
  • 7
  • 18
  • "Which could get messy since a dynamically allocated string could be a source of memory leak." And this is why OP shouldn't use `char*` as pointed out by @tomalak – Lambdageek Jun 20 '11 at 23:11