parameter
parameter

Reputation: 904

Crash when replacing filename extension

I'm trying to create strings that replace my .png and .jpg files in a directory (all files in it only contain these extensions) with .txt using the .replace command like so:

//say path is directory
path.replace(path.end()-3, path.end()-1, "txt");

It keeps crashing my program though, what am I doing wrong? It's finding the 'png' part properly, but the replacing is not working.

enter image description here

Here's what happens when I do this.

string a = dir.getPath(i); //this is ..\data\images\Test0.png
string b = dir.getPath(i).replace(dir.getPath(i).end()-3, dir.getPath(i).end(), "txt"); //crashes

enter image description here

Upvotes: 2

Views: 454

Answers (4)

Casey
Casey

Reputation: 10936

For readers from the future:

Use the <filesystem> library to do this for you in one call to std::filesystem::path::replace_extension:

std::filesystem::path d = /* Path to directory */;
for(auto& dir_entry : std::filesystem::directory_iterator{d}) {
    if(dir_entry.is_regular_file()) {
        auto& p = dir_entry.path();
        const auto ext = p.extension();
        if(ext == ".jpg" || ext == ".png") {
            p.replace_extension(".txt");
        }
    }
}

Upvotes: 0

Vlad from Moscow
Vlad from Moscow

Reputation: 310950

It can be done the following way

#include <iostream>
#include <string>

int main() 
{
    std::string s( "..\\data\\images\\Test0.png" );

    std::cout << s << std::endl;

    std::string::size_type pos;

    if ( ( ( pos = s.rfind( ".png" ) ) != std::string::npos ) ||
         ( ( pos = s.rfind( ".jpg" ) ) != std::string::npos ) )
    {        
        s.replace( pos, std::string::npos, ".txt" );
    }

    std::cout << s << std::endl;

    return 0;
}

The output is

..\data\images\Test0.png
..\data\images\Test0.txt

Upvotes: 0

orfdorf
orfdorf

Reputation: 1010

Remove the -1 from your second argument.

string path = "filename.png";
path.replace(path.end() - 3, path.end(), "txt");

Results in path storing:

"filename.txt"

Because the first argument indicates where to start replacing characters, the second argument indicates where to stop (you stop AFTER replacing the last character, not 1 position before it), and the last argument specifies what to replace it with.

UPDATE: In response to your updated question, your problem can be answered by asking your self what does dir.getPath(i) return? A new instance of a string. You're trying to traverse from an iterator in one string to an iterator in another string.

Upvotes: 1

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726509

If you want to replace the last three characters, you need to provide a range that has three characters. Currently, your range is only two characters in size, i.e from end()-3, inclusive, to end()-1, exclusive.

string s("hello.png");
s.replace(s.end()-3, s.end(), "txt");
cout << s << endl;

In addition, you need to make sure that the length of the string is not less than three characters, otherwise accessing end()-3 is undefined behavior.

Also, make sure that you do not use dir.getPath(i) multiple times, otherwise your end()-3 iterator and end() iterator point to different strings. I.e.

string b = dir.getPath(i).replace(dir.getPath(i).end()-3, dir.getPath(i).end(), "txt"); // Crashes
//             ^^^^^^               ^^^^^^                  ^^^^^^^
//            Copy # 1             Copy # 2                 Copy # 3

needs to be

string b = dir.getPath(i);
b.replace(b.end()-3, b.end(), "txt"); // Does not crash

Demo.

Upvotes: 1

Related Questions