-1

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?

  • 1
    Can you provide more detail than "this doesn't work"? – Scott Hunter Feb 08 '22 at 00:06
  • 1
    Every time you find an extra space, you need to shift all the following characters in the string to the left. – Barmar Feb 08 '22 at 00:10
  • Well strings are new to me. I'm beginner and this simple task is hard for me. Could anyone write me program with simple explanation? –  Feb 08 '22 at 00:10
  • 1
  • 1
    You need two positions — the one you're reading from and the one you're writing to. At first they're synchronized, but they become unsynchronized once you come across multiple spaces. Since you're only removing characters, the operation can be done in situ in safety. – Jonathan Leffler Feb 08 '22 at 00:12
  • Don't forget to NUL-terminate the shortened string. – Weather Vane Feb 08 '22 at 00:13
  • I edited my code, do you know how to fix this? –  Feb 08 '22 at 00:37
  • *"This code doesn't remove the last space. How to fix this?"* Check the last character when you terminate the string. – Weather Vane Feb 08 '22 at 00:39
  • @WeatherVane how to check the last character? –  Feb 08 '22 at 00:40
  • `if(x > 0 && str[x-1] == ' ') str[x-1] = '\0';` – Weather Vane Feb 08 '22 at 00:41
  • thanks, now the problem is that this doesn't work for the empty string –  Feb 08 '22 at 00:44
  • Check for the empty string. – Weather Vane Feb 08 '22 at 00:46
  • I edited question, could someone fix code? –  Feb 08 '22 at 00:52
  • You will learn more by doing the work yourself. You've made an effort; that is indisputable. But pleading for someone to fix your code is unlikely to achieve your goal and will quite likely mean you get downvotes. – Jonathan Leffler Feb 08 '22 at 01:10
  • Run your code in a debugger. It will show you what is happening, and from there, you should be able to fix the problem. – Jonathan Leffler Feb 08 '22 at 01:10
  • My testing of the code in [Revision 4](https://stackoverflow.com/revisions/71026982/4) of the question indicates that it correctly handles empty strings and strings of all blanks — 1 and many blanks. The was one test case that it didn't handle correctly, namely `"abc def"` (with two blanks in the middle; that was not null-terminated properly so I got `abc deff` as the output. – Jonathan Leffler Feb 08 '22 at 01:16
  • I added a few more test cases and got more erroneous output: `Before: [abc def]` and `After: [abc defef]` —— `Before: [abc xyz def]` and `After: [abc xyz def def]`. One fix for that is simply to add `str[x] = '\0';` before the `return` in the function. Note that you were already reminded (told) to ensure you null-terminate the string – your code doesn't. – Jonathan Leffler Feb 08 '22 at 01:21

3 Answers3

0

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.

Chris
  • 26,361
  • 5
  • 21
  • 42
  • could you put the whole program as answer? –  Feb 08 '22 at 01:20
  • this returns with one extra space on the end –  Feb 08 '22 at 01:44
  • Edited to include `main` and example terminal output from running it. – Chris Feb 08 '22 at 01:46
  • I get an error: The program accesses a variable that is not initialized for this line: while (isspace(*ch)) *ch-- = '\0'; –  Feb 08 '22 at 02:02
  • An error, or a warning? Though I'm not sure where either comes from, as `ch` is the only variable in play, and it's definitely initialized. – Chris Feb 08 '22 at 02:07
  • I have strong autotest defined in the development environment of my university –  Feb 08 '22 at 02:11
  • thank you very much, this solved my task –  Feb 08 '22 at 02:11
0

Basically, you need to remove the consecutive space characters between the words in the input string and all leading and trailing space characters of the input string. That means, write code to remove the consecutive space characters in the input string and while removing the consecutive space characters, remove the leading and trailing space characters completely.

You can do it in just one iteration. No need to write the different functions for removing the leading and trailing spaces of input string, as shown in the other post.

You can do:

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

char * eject (char *str) {
    if (str == NULL) {
        printf ("Invalid input..\n");
        return NULL;
    }

    /* Pointer to keep track of position where next character to be write 
     */
    char * p = str;
    for (unsigned int i = 0; str[i] ; ++i) {
        if ((isspace (str[i])) && ((p == str) || (str[i + 1] == '\0') || (str[i] == (str[i + 1])))) {
            continue;
        }
        *p++ = str[i];
    }

    /* Add the null terminating character. 
     */
    *p = '\0';
    return str;
}

int main (void) {
  char s[] = "  abcde    abcde    ";
  printf("\"%s\"\n", eject(s));

  char s1[] = "      ";
  printf("\"%s\"\n", eject(s1));

  char s2[] = "ab  yz   ";
  printf("\"%s\"\n", eject(s2));

  char s3[] = "  ddd xx  jj m";
  printf("\"%s\"\n", eject(s3));

  char s4[] = "";
  printf("\"%s\"\n", eject(s4));

  return 0;
}

Output:

# ./a.out
"abcde abcde"
""
"ab yz"
"ddd xx jj m"
""
H.S.
  • 11,654
  • 2
  • 15
  • 32
0

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
Lundin
  • 195,001
  • 40
  • 254
  • 396