Dess
Dess

Reputation: 2649

Move a chunk of text within a std::string to the left by X amount of chars

What is an efficient way to shift the text within a std::string, starting at some offset, to the left by X amount of characters?

Example:

(Some sTring here)

Shifted to the left by 1, starting at T, would yield:

(Some Tring here)

Background Info

In a compiler for a text pattern language, in which text of the form:

(some expression here)

represent expressions to be replaced with some value. Those expressions can appear interspersed with text; something like the following:

There are ($count) types of things.

The above would be replaced with something like the following, after the text is evaluated by the compiler.

There are 42 types of things.

Now, I want support the ability to 'escape' expressions, so that text that would normally represent expressions can be written verbatim. So this:

There are \($count) types of things.

would be outputted like this:

There are ($count) types of things.

Basically, all that needs to happen is for the text after There are to be moved by 1 byte to the left. I was trying to think of an efficient way to do this. I wish I could memmove std::string's data buffer from '(...' by 1 byte to the left, but I'm not sure if that's possible.

I hate the idea of copying a big chunk of a string just to move the contents to the left by 1 character.

So, I'm hoping there is a better way than to do something like this:

auto size = buffer.size()
auto temp = buffer.substr(parentPosistion, size - parentPosistion)
buffer.assign(temp, parentPosistion - 1)
buffer.resize(size - 1)

Upvotes: 1

Views: 1820

Answers (3)

Mateusz Grzejek
Mateusz Grzejek

Reputation: 12058

The best way to do this is to use string::erase to remove slash and then proceed with further operations. erase internally should be an efficient memmove, since string's data is stored in continuous block of memory.

Example:

string s = "There are \\{$count} types of things.";
cout << s << endl;

size_t slash_pos = s.find("\\");

if(slash_pos != string::npos)
    s.erase(slash_pos, 1);

cout << s << endl;

Output:

There are \{$count} types of things.
There are {$count} types of things.

Working sample.


You did not specified the amount of input data, but yes - this approach can be slow for large strings, containing multiple slashes.

In such case, you would want to remove all slashes in single run. It is possible with std::remove:

string s = "\\{$count1} \\{$count2} \\{$count3}";
cout << s << endl;

s.erase(std::remove(s.begin(), s.end(), '\\'), s.end());

cout << s << endl;

Output:

\{$count1} \{$count2} \{$count3}
{$count1} {$count2} {$count3}

Explanation: std::remove(b, e, v) transforms range [b; e) by removing all elements with value equal to v and returns iterator to end of new sequence. Then, we use this iterator to remove redundant characters. For example, if you simply called:

std::remove(s.begin(), s.end(), '\\');

Second output would be:

{$count1} {$count2} {$count3}t3}

Upvotes: 2

vsoftco
vsoftco

Reputation: 56547

Here is a way using std::rotate, will shift left at some offset by an arbitrary amount X, reducing the length of the string by X

#include <algorithm>
#include <iostream>
#include <string>

void shift_left(std::string& str, std::size_t offset, std::size_t X)
{
    std::rotate(std::next(str.begin(), offset),
                std::next(str.begin(), offset + X),
                str.end() );
    str = str.substr(0, str.size() - X);
}

int main()
{
    std::string str = "Some sTring here";
    shift_left(str, 5, 2); // Outputs: Some ring here
    std::cout << str << std::endl;
}

Upvotes: 1

MikeMB
MikeMB

Reputation: 21146

If you want to remove e.g. all back slashes, then the shortest solution I can think of is this:

#include <algorithm>
#include <string>
#include <iostream>

int main()
{
    std::string str = "There are \\{$count} types of things.";

    str.erase(std::remove(str.begin(), str.end(), '\\'),str.end());

    std::cout << str << std::endl;
}

And I doubt there is a more efficient solution. If you want to remove multiple different characters in one go, you can use remove_if.

Upvotes: 0

Related Questions