5
\$\begingroup\$

3 string trimming functions:
Trim whitespace from the beginning of a string.
Trim whitespace from the end of a string.
Trim whitespace from both ends of a string.

Looking to improve code for:

  1. Correctness (tested code - but so far no holes) - especially overlapping src/dest cases.

  2. Design

  3. Portability (3 tried: MS, *nix, embedded)

  4. Efficiency

Note: (unsigned char) used as unless x==EOF, isspace(x) is not defined when x < 0.

/* String trim functions
 * 
 * Result is sized limited.
 * Overlapping source and destination is OK.
 * Pointer parameters are assumed to point to valid strings spaces.
 */

#include <ctype.h>
#include <stddef.h>
#include <string.h>

// Return string beginning with first non-white-space in string `src`
// Return NULL when destination is too small, `dest` unchanged.

char *trim_begin(char *dest, size_t dest_size, const char *src) {
  while (isspace((unsigned char) *src)) {
    src++;
  }
  size_t src_size = strlen(src) + 1;
  if (src_size > dest_size) {
    return NULL;
  }
  return memmove(dest, src, src_size);
}

// Return string beginning string `src` but not including any of its trailing 
//   white-spaces.
// Return NULL when destination is too small, `dest` unchanged.

char *trim_end(char *dest, size_t dest_size, const char *src) {
  size_t len = strlen(src);
  while (len > 0 && isspace((unsigned char) src[len - 1])) {
    len--;
  }
  size_t src_size = len + 1;
  if (src_size > dest_size) {
    return NULL;
  }
  memmove(dest, src, len);
  dest[len] = 0;
  return dest;
}

// Return string beginning with first non-white-space in string `src` but not 
//   including any of its trailing white-spaces.
// Return NULL when destination is too small, `dest` unchanged.

char *trim(char *dest, size_t dest_size, const char *src) {
  while (isspace((unsigned char) *src)) {
    src++;
  }
  return trim_end(dest, dest_size, src);
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Use character constants for readability: dest[len] = 0; -> dest[len] = '\0'; \$\endgroup\$
    – chqrlie
    Commented May 6, 2015 at 14:50
  • \$\begingroup\$ @chqrlie Agreed Ref \$\endgroup\$
    – chux
    Commented May 6, 2015 at 15:26

2 Answers 2

2
\$\begingroup\$

Looks good to me.

A couple of (very) minor points: you should say in the comments that src mustn't be NULL; you don't need src_size in trim_end if you use the test 'if (len>=dest_size)'.

\$\endgroup\$
3
  • \$\begingroup\$ "should say in the comments that src mustn't be NULL". OK - though about that before-hand. In multiple places, comments say "Trim whitespace from ... a string" and " string src", Thought that was sufficient to imply src must be a pointer to a string ("contiguous sequence of characters terminated by and including the first null character.") and not NULL. Yet begin explicit may help. \$\endgroup\$
    – chux
    Commented May 6, 2015 at 1:19
  • 1
    \$\begingroup\$ I suppose some might think that since there are no characters, therefore no spaces, in a NULL string, it would be safe to trim it and you'd get NULL back, which might be better behaviour than a crash. \$\endgroup\$
    – dmuir
    Commented May 6, 2015 at 8:07
  • \$\begingroup\$ Adding a well behaved trim(NULL) is a interesting idea and I generally favor it. Yet standard string functions like strcat(ptr, NULL), strcmp(NULL, NULL) do not include defined behavior and I was following that model. \$\endgroup\$
    – chux
    Commented May 6, 2015 at 15:31
4
\$\begingroup\$
  • Repeated code

    size_t src_size = strlen(src) + 1;
    if (src_size > dest_size) {
      return NULL;
    }
    return memmove(dest, src, src_size);
    

    is a good candidate for a function (safe_memmove or something similar).

- Optimistic loops while (isspace((unsigned char) *src)) may never stop: '\0' is not a white space. Test for end of string explicitly:

    while (*src && isspace((unsigned char) *src))

Red face above. I don't know what I was thinking.

  • Portability. A free-standing environment doesn't have to provide memmove or strlen or isspace. Only features confined in <float.h>, <iso646.h>, <limits.h>, <stdarg.h>, <stdbool.h>, <stddef.h>, and <stdint.h> are mandatory. I am afraid that to claim embedded portability you need to implement optional features yourself.

    Sorry for nitpicking.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ If '\0' is not a white space, then while (isspace(*src)) will end when it reaches the '\0'. So I don't understand the "Optimistic loops" objection. \$\endgroup\$
    – JS1
    Commented May 5, 2015 at 2:10
  • \$\begingroup\$ @JS1 You are absolutely correct. I don't understand it either. \$\endgroup\$
    – vnp
    Commented May 5, 2015 at 6:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.