'Eject excess space from string in C

I need to write a function which will eject excess space from string in C.

Example:

char s[]="  abcde    abcde    "; 

OUTPUT:

"abcde abcde"

Code:

#include <stdio.h>
#include <ctype.h>
char *eject(char *str) {
  int i, x;
  for (i = x = 0; str[i]; ++i)
    if (!isspace(str[i]) || (i > 0 && !isspace(str[i - 1])))
      str[x++] = str[i];
  if(x > 0 && str[x-1] == ' ') str[x-1] = '\0';
  return str;
}

int main() {
  char s[] = "  abcde    abcde    ";
  printf("\"%s\"", eject(s));
  return 0;
}

This code doesn't work for string " " If this string is found program should print:

""

How to fix this?



Solution 1:[1]

You could write two functions which trim leading and trailing whitespace characters.

void trim_front(char *src) {
    size_t i = 0, j = 0;
    while (isspace(src[i])) i++;
    while (i < strlen(src)) src[j++] = src[i++];
    src[j] = '\0';
}

void trim_back(char *src) {
    char *ch = src + strlen(src) - 1;
    while (isspace(*ch)) *ch-- = '\0';
}

If you know you don't have to deal with trailing or leading spaces, your task becomes much simpler.

void reduce_spaces(char *src) {
    size_t i = 0, j = 0;

    for (; i < strlen(src); ++i) {
        if (i == strlen(src) - 1 || 
            (isspace(src[i]) && !isspace(src[i + 1])) ||
            !isspace(src[i])) {
            src[j++] = src[i];
        }
    }

    src[j] = '\0';
}

And testing this:

int main(void) {
    char s[] = "     hello    world     ";

    trim_front(s);
    trim_back(s);
    reduce_spaces(s);

    printf(">%s<\n", s);

    return 0;
}
% gcc test.c
% ./a.out
>hello world<
%

Of course, if you really want to, you can transplant the code from those functions into reduce_spaces, but decomposing a problem into multiple smaller problems can make things much easier.

Solution 2:[2]

A slightly more advanced answer just for reference - suppose you were tasked in writing a professional library for the use in real world programs. Then one would first list all requirements that make sense:

  • It's good practice to treat strings as "immutable" - that is, build up a new string based on the old one rather than in-place replacement.
  • Take the destination string as parameter but also return a pointer to it (similar to strcpy etc functions).
  • In case of empty strings, set the destination string empty too.
  • Remove all "white space" not just the ' ' character.
  • Instead of always inserting a space character after each word, why not insert a variable delimiter? Might as well be something like , or ;.
  • No delimiter should be inserted after the last word.
  • The algorithm should only traverse the data once for performance reasons. That is, internal calls like strlen etc are unacceptable.
  • Byte by byte iteration is fine - we need not care about alignment.

Then we might come up with something like this:

#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>

char* trim_delimit (char* restrict dst, const char* restrict src, char delim)
{
  char* start = dst;
  *dst = '\0';
  
  bool remove_spaces = true;
  char* insert_delim_pos = NULL;
  
  for(; *src != '\0'; src++)
  {
    if(remove_spaces)
    {
      if(!isspace(*src))
      {
        remove_spaces = false;
        if(insert_delim_pos != NULL)
        {
          // we only get here if more words were found, not yet at the end of the string
          *insert_delim_pos = delim; 
          insert_delim_pos = NULL;
        }
      }
    }

    if(!remove_spaces)
    {
      if(isspace(*src))
      {
        remove_spaces = true;
        insert_delim_pos = dst; // remember where to insert delimiter for later
      }
      else
      {
        *dst = *src;
      }
      dst++;
    }
  }

  return start;
}

Test cases:

int main (void)
{
  char s[]="  abcde    abcde    "; 
  char trimmed[100];

  puts(trim_delimit(trimmed, s, ' '));
  puts(trim_delimit(trimmed, "", ' '));
  puts(trim_delimit(trimmed, s, ';'));
}

Output:

abcde abcde

abcde;abcde

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Lundin