Remove extra spaces in the string in-place by C++

Evgeny Chekunov
2 min readJul 1, 2021

Given a string with extra spaces. It is needed to remove all extra spaces (including trailing and leading spaces) so that all words have only a single space between them. We cannot use additional space. All manipulations should be done in-place. Also, the original string can have any punctuations.

Input: " This is   my string on Medium !  "Output: "This is my string on Medium!"

I want to propose one elegant way to solve it. We will build a Finite State Machine. So here the algorithm:

1. We will iterate over the string — symbol by symbol.

2. Define three states: Idle — initial state, Space — last character was a space, Symbol — the character is a valid symbol.

3. Also we defined an index to manage the current position in the original string.

4. In the main loop while we have any symbol in the string check the current symbol:

- if space and current state Idle or Space continue the loop (ignore all leading, trailing, and spaces within the string)

- if space and the current state is not Idle or Space change state to Space

- else if the current symbol is a punctuation mark and the last state was Space, decrease an index by one to remove space before punctuation mark otherwise change state to Symbol

- place the current character in the string at index and increase the index by one

5. Check the last state after the loop finished. If the state was Space decrease index by one to remove last space.

6. Remove characters in the original string from the index to the last character.

#include <iostream>
#include <string>
#include <string_view>

bool isPunctuation(char ch) {
static constexpr std::string_view punctuation = ".,!?:;";
return punctuation.find(ch) != punctuation.npos;
}

void removeExtraSpaces(std::string& str)
{
enum {
Idle,
Space,
Symbol
} state = Idle;

size_t index = 0;
for (const auto ch : str) {
if (isspace(ch)) {
if (state == Idle || state == Space) {
continue;
}
state = Space;
} else {
if (isPunctuation(ch) && state == Space) {
index--;
}
state = Symbol;
}
str[index++] = ch;
}

if (state == Space) {
index--;
}

str.erase(str.begin() + index, str.end());
}

And some test cases:

struct TestParams {
std::string testValue;
std::string expectedValue;
};

int main() {
TestParams params[] = {
{" This is my string", "This is my string"},
{" \t \nThis is\n my string! \t ", "On my home world!"},
{"This is my string !? ", "This is my string!?"},
{" \t \n ", ""},
{"", ""},
{" ", ""}
};

for (size_t i = 0; i < sizeof(params)/sizeof(TestParams); i++) {
std::cout << "Run test case #" << i + 1 << ": ";
removeExtraSpaces(params[i].testValue);
if (params[i].testValue == params[i].expectedValue) {
std::cout << "PASSED";
} else {
std::cout << "FAILED\n" <<
"with result [" << params[i].testValue << "]" << std::endl;
}
std::cout << std::endl;
}

return 0;
}

--

--