user1939168
user1939168

Reputation: 557

Remove all spaces before a character in a line

I have this peculiar requirement where I need to remove all space characters before a specific character which is a pipe'|'. I have written a test code for it which is actually printing the right output but additionally gifting me a core file :(

My code is below:

int main()
{

    string line="1  2  |3 4| hbvhwf     wjff wenf|hjbcwbfw     ejwef   efwk    dfkwe|jsv                       |";
    cout <<line<<endl;
    string::iterator ite =(line.begin());

    int counter=0;
    int index=0;
    int start=0;
    while(ite != (line.end()))
    {
        if(*ite == '|' && counter > 0)
        {
            line.erase(start,counter);
            counter=0;
            cout<<line<<endl;
        }
        if(ite!=line.end())
        {
            if(isalnum(*ite))
            {
                counter=0;
            }
            if(*ite==' ')
            {
                if(!counter)
                {
                    start=index;
                }
                counter++;
            }
            ite++;
            index++;        
        }
    }

    cout<<line<<endl;
}

I am just going nuts in finding the root cause of the core dump. Could anybody please help? expected output is:

1  2|3 4| hbvhwf     wjff wenf|hjbcwbfw     ejwef   efwk    dfkwe|jsv|

Upvotes: 1

Views: 135

Answers (3)

jfly
jfly

Reputation: 7990

As Krzysztof's answer says, the reason of the core dump is the erase() invalidated the iterators. To solve this, you need to reset the iterator correctly, use the range version of erase(), your will get an iterator referring to the character that now occupies the position of the first character erased, and assign it to ite. Change the first if statement to the code below, it should work fine.

    if(*ite == '|' && counter > 0)
    {
        ite = line.erase(ite - counter, ite);

        counter=0;
        cout<<line<<endl;
    }

Upvotes: 1

BlackMamba
BlackMamba

Reputation: 10252

#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
using namespace std;

int main()
{
    string line="1  2  |3 4| hbvhwf     wjff wenf|hjbcwbfw     ejwef   efwk    dfkwe|jsv                       |";
    cout <<line<<endl;

    string res;
    size_t length =  line.size();
    bool flag = false;
    for (int i = length - 1; i >= 0 ; --i)
    {
        if (line[i] == '|')
        {
            res.push_back(line[i]);
            flag = true;
        }
        else if (flag && line[i] == ' ')
        {

        }
        else
        {
            res.push_back(line[i]);
            flag = false;
        }
    }

    copy(res.rbegin(), res.rend(), ostream_iterator<char>(cout, ""));
    cout<<endl;
}

The ouput is:

1  2  |3 4| hbvhwf     wjff wenf|hjbcwbfw     ejwef   efwk    dfkwe|jsv                       |
1  2|3 4| hbvhwf     wjff wenf|hjbcwbfw     ejwef   efwk    dfkwe|jsv|

Upvotes: 0

Krzysztof Kosiński
Krzysztof Kosiński

Reputation: 4325

Calling erase() on a string invalidates all iterators into the string, including ite.

Specifically, when line.erase(start,counter); executes, ite is invalidated - it is no longer guaranteed to refer to a valid position in the string. If it's not equal to line.end(), it can be dereferenced in the condition isalnum(*ite). Because ite is invalidated at that point, it can refer to memory which is already freed (e.g. if the string was reallocated after being erased). Therefore this line causes a segfault.

Here is a simpler version of your code, which does not have the problem.

std::string line = ...;
unsigned space_run = 0;

for (unsigned i = 0; i < line.size(); ++i) {
    if (line[i] == ' ') {
        ++space_run;
    } else if (line[i] == '|') {
        line.erase(i - space_run, space_run);
        i -= space_run;
        space_run = 0;
    } else {
        space_run = 0;
    }
}

Upvotes: 1

Related Questions