C/C++ Arena

Step 10 of 10

Challenge: squeeze out spaces

Another two-pointer classic, with both pointers moving in the same direction at different speeds:

Because the write pointer never gets ahead of the read pointer, you can filter a string in place, in the same buffer, without allocating anything.

#include <stdio.h>

void keep_digits(char *s) {
    char *write = s;
    for (const char *read = s; *read != '\0'; read++) {
        if (*read >= '0' && *read <= '9') {
            *write = *read;
            write++;
        }
    }
    *write = '\0';
}

int main(void) {
    char phone[] = "(555) 123-4567";
    keep_digits(phone);
    printf("%s\n", phone);
    return 0;
}
5551234567

Trace a few steps

For "(555) 1": the read pointer sees (, which isn't kept, so write stays at index 0. Then 5, kept: written at index 0, write moves to 1. Then 5 to index 1, 5 to index 2. Then ) and the space are skipped, and 1 goes to index 3. The kept characters slide left over the ones that were dropped.

Don't forget the terminator

When the read pointer reaches the end, the characters after the write position are leftovers from the original string. Writing '\0' at the write position cuts them off. Forgetting it is the classic bug: the result would print with junk on the end.

phone is declared as an array (char phone[] = "..."), which makes a writable copy of the text. Modifying a string literal directly through a pointer would be undefined behavior.

Your turn: write void squeeze(char *s) that removes every space from s in place.

Previous: Two pointers from both ends