0

I have a char-array and I want to remove whitespace just before or after or both, the word (or phrase) and not in the middle.

For example:

"hello" --> "hello"

" hello" --> "hello"

" hello " --> "hello"

" " --> ""

This is my code:

#include <stdlib.h>
#include <string.h>

int main(void) 
{
    char s[] = " prova ";
    char *t = NULL;
    if (s == NULL)
    {
        t = NULL;
    }
    else {
        int n = strlen(s);
        t = malloc(sizeof(char)* (n + 1));
        for (int i = 0; i <= n; ++i)
        {
            t[i] = s[i];
        }
        int k = 0;
        if (s[0] == ' ')
        {
            ++k;
            t = realloc(t, sizeof(char)*n);
            for (int i = 0; i <= n - 1; ++i)
            {
                t[i] = s[i + 1];
            }
        }

        if (s[n - 1] == ' ')
        {
            if (k == 1)
            {
                int j = 0;
                t = realloc(t, sizeof(char)*(n - 1));
                for (int i = 0; i <= n - 2; ++i)
                {
                    t[i] = t[i];
                    j = i;
                }
                t[j] = 0;
            }
            else
            {
                int j = 0;
                t = realloc(t, sizeof(char)*n);
                for (int i = 0; i <= n - 1; ++i)
                {
                    t[i] = t[i];
                    j = i;

                }
                t[j] = 0;
            }
        }
    }
    return t;
}

The debugging does not give me error or other things, but I know there is a problem with memory and heap and I don't know how to remove it.

I looked for other questions similar to mine on this platform and they exist, but none of the answers solved my problem.

Please give me some advice, thank you

KKKKK
  • 273
  • 2
  • 18

4 Answers4

1

There is two ways of doing this:

  • First: Trim original variable inside other variable, so there are two variables: first variable have a string with whitespaces and second string will be the content of first varaible without start/end spaces.
  • Second: Trim the variable inside itself.

The code which do that is:

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

size_t trimInOtherVariable(char *out, size_t len, const char *str)
{
  if(len == 0)
    return 0;

  const char *end;
  size_t out_size;

  // Trim leading space
  while(isspace((unsigned char)*str)) str++;

  if(*str == 0)  // All spaces?
  {
    *out = 0;
    return 1;
  }

  // Trim trailing space
  end = str + strlen(str) - 1;
  while(end > str && isspace((unsigned char)*end)) end--;
  end++;

  // Set output size to minimum of trimmed string length and buffer size minus 1
  out_size = (end - str) < len-1 ? (end - str) : len-1;

  // Copy trimmed string and add null terminator
  memcpy(out, str, out_size);
  out[out_size] = 0;

  return out_size;
}



char *trimInSameVariable(char *str)
{
  char *end;

  // Trim leading space
  while(isspace((unsigned char)*str)) str++;

  if(*str == 0)  // All spaces?
    return str;

  // Trim trailing space
  end = str + strlen(str) - 1;
  while(end > str && isspace((unsigned char)*end)) end--;

  // Write new null terminator character
  end[1] = '\0';

  return str;
}



int main(void)
{
    // Declare string for trimming
    char buffer[] = "    pr ova    ";
    size_t size;

    // Declare pointers which will store trimmed variable
    char *stringWithSpaces = (char *) malloc( sizeof(char)*(strlen(buffer)+1) );
    char *stringWithoutSpaces = (char *) malloc( sizeof(char)*(strlen(buffer)+1) );

    // Check if allocating memory is OK. Then copy string to trim inside pointer
    if (stringWithSpaces == NULL || stringWithoutSpaces == NULL)
        return -1;
    else
        strcpy(stringWithSpaces, buffer);

    // Way 1: Trim 'stringWithSpaces' inside 'stringWithoutSpaces'
    size = trimInOtherVariable(stringWithoutSpaces, strlen(buffer), stringWithSpaces);
    // Way 2: Trim 'stringWithSpaces' inside itself
    stringWithSpaces = trimInSameVariable(stringWithSpaces);

    // Show trimmed strings
    printf (
        "String trimmed in other variable: \"%s\"\n"
        "String trimmed in same variable: \"%s\"\n"
        , stringWithoutSpaces, stringWithSpaces
    );

    // End function
    return 0;
}

Hope this help you.

JuMoGar
  • 1,740
  • 2
  • 19
  • 46
  • thank you, your program works, now I'll try to figure it out to fit my schedule, thanks again – KKKKK Jan 27 '19 at 18:44
  • I put +1 on the answer, but I accepted David's answer because it was more explanatory than yours. Sorry. – KKKKK Jan 27 '19 at 18:59
1

Your program has at least these errors:

  1. #include directives are missing. You need stdlib.h and string.h at the very least.
  2. main is declared to return int, but you are returning NULL from one place and t (which is of type char*) from another place.
  3. It may or may not do what it's supposed to do. It only removes up to one space in the beginning and one in the end, not all spaces before or after the phrase, and does so in a needlessly convoluted way.

One kind of error this program does not seem to have is writing data past the end of anything.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • I forgot to put the directives here in the questions, but in my pragram there are, now I edit – KKKKK Jan 27 '19 at 18:34
  • For the second point you are right, now I edit – KKKKK Jan 27 '19 at 18:37
  • For the third point how do I work to solve it, can you give me some advice? – KKKKK Jan 27 '19 at 18:38
  • Three steps: 1. *Find* all the spaces at the front of the *original* string. 2. *Find* all the spaces at the back of the *original* string. 3. Make a *new* string to accommodate what's left, and make a copy. – n. m. could be an AI Jan 27 '19 at 20:00
1

There are a large number of small errors in your code beginning with:

if (s == NULL)

s can never be NULL unless your compiler is completely broken or you have less than an 8-byte stack.

Next, you realloc before you remove the leading whitespace, e.g.

        t = realloc(t, sizeof(char)*n);
        for (int i = 0; i <= n - 1; ++i)

Which (if any) bytes are truncated by the call to realloc are not specified. Instead, you need to operate on t to remove the leading whitespace before calling realloc (and then you are still not guaranteed any memory will be adjusted)

Next, you call realloc multiple times, when you should simply operate on the original copy of s in t to remove both leading/trailing whitespace and then make a single call to realloc at the end. malloc/realloc are relatively expensive calls from an efficiency standpoint and shouldn't be called repetitively.

Rearranging the logic a bit, you could do:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) 
{
    char s[] = " prova ";
    char *t = NULL;
    size_t n = strlen(s);           /* strlen returns size_t */
    int k = 0;

    t = malloc (n + 1);             /* sizeof(char) is 1 */
    if (t == NULL) {                /* validate ALL allocations */
        perror ("malloc-t");
        return 1;
    }

    for (size_t i = 0; i <= n; i++) /* copy s to t */
        t[i] = s[i];

    while (t[k] == ' ')             /* count leading whitespace */
        k++;

    for (size_t i = 0, j = k; j <= n; i++, j++) /* remove whitespace */
        t[i] = t[j];

    n -= k;                         /* update n */

    while (n && t[n - 1] == ' ')    /* remove trailing whitespace */
        t[n-- - 1] = 0;

    void *tmp = realloc (t, n + 1); /* realloc with tmp varaible */
    if (tmp == NULL) {              /* validate ALL allocations */
        perror ("realloc-t");
        return 1;
    }
    t = tmp;                        /* assign new block to t */

    printf ("t: '%s'\n", t);
    free (t);                       /* don't forget to free memory */

    return (int)n;
}

Example Use/Output

$ ./bin/str_trim_realloc
t: 'prova'

Memory Use/Error Check

$ valgrind ./bin/str_trim_realloc
==26078== Memcheck, a memory error detector
==26078== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==26078== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==26078== Command: ./bin/str_trim_realloc
==26078==
t: 'prova'
==26078==
==26078== HEAP SUMMARY:
==26078==     in use at exit: 0 bytes in 0 blocks
==26078==   total heap usage: 2 allocs, 2 frees, 14 bytes allocated
==26078==
==26078== All heap blocks were freed -- no leaks are possible
==26078==
==26078== For counts of detected and suppressed errors, rerun with: -v
==26078== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • @DavidCRankin Thank you man, now I do not give any memory error, now I study your program for good to understand how you have solved it, thanks again – KKKKK Jan 27 '19 at 18:53
  • 1
    Sure. Glad to help. The easiest way to work with this type problem is to take out a pencil and paper and draw the characters in `t` out on paper. Then you can see and verify what you need to do with each character in each loop iteration. That really helps keep the indexing straight. When I was learning, I would just use a small box around each character to represent each character in a string and write the index below. Makes it a lot easier (and less error prone) than trying to do it all in your head `:)` – David C. Rankin Jan 27 '19 at 19:00
0

You must free 't' before the end of the program. Maybe you need a little refactoring in your code:

#include <stdlib.h> //realloc
#include <string.h> // memcpy

char *remove_space(char *s) {
  char *t = 0;
  if (s == 0) {
    return 0;
  } else {
    int n = strlen(s);
    t = malloc(sizeof(char) * n + 1);
    for (int i = 0; i <= n; ++i) {
      t[i] = s[i];
    }
    int k = 0;
    if (s[0] == ' ') {
      ++k;
      t = realloc(t, sizeof(char) * n);
      for (int i = 0; i <= n - 1; ++i) {
        t[i] = s[i + 1];
      }
    }

    if (s[n - 1] == ' ') {
      if (k == 1) {
        int j = 0;
        t = realloc(t, sizeof(char) * n - 1);
        for (int i = 0; i <= n - 2; ++i) {
          t[i] = t[i];
          j = i;
        }
        t[j] = 0;
      } else {
        int j = 0;
        t = realloc(t, sizeof(char) * n);
        for (int i = 0; i <= n - 1; ++i) {
          t[i] = t[i];
          j = i;
        }
        t[j] = 0;
      }
    }
  }

  return t;
}

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

  free(remove_space(s));

  return 0;
}

Check with valgrind:

valgrind --leak-check=full --show-reachable=yes ./program
==8753== Memcheck, a memory error detector
==8753== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8753== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==8753== Command: ./program
==8753== 
==8753== 
==8753== HEAP SUMMARY:
==8753==     in use at exit: 0 bytes in 0 blocks
==8753==   total heap usage: 3 allocs, 3 frees, 21 bytes allocated
==8753== 
==8753== All heap blocks were freed -- no leaks are possible
==8753== 
==8753== For counts of detected and suppressed errors, rerun with: -v
==8753== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)