John said:
It's clean, concise, and looks good.
But you have no safeguard against memory having no \0 byte. Your code
can go into an infinite loop. Mine cannot.
I'd call that user error. They should pass a pointer to a string. If
you attempt to prevent an infinite loop by choosing an upper bound, then
you have the impossibility of success for a programmer trying to trim a
string where that constraint isn't met.
We'll likely just disagree about it; I think the responsibility should
be with the programmer using the function, so that they can trim any
string using well-defined operations, such as pointer arithmetic without
overflow.
My code may not be as pretty,
Says who?
but I don't think you can match its run
time performance.
I don't know of a guarantee that the read and write passes (if any) in
'memmove' are faster than one's own read and write passes.
Without regarding security, the possibility that 'memmove' might be
slower than one's own single read and write passes, and prevention of an
infinite loop because of a programmer error, here is another version
which calls 'memmove' multiple times and uses an arbitrary window size
of one's choice (up to the maximum value that can fit inside a
'size_t'). It'll keep trying to trim forever if no null terminator is
found, but it should allow one to trim a string many times greater than
'SIZE_MAX'. Since 'SIZE_MAX' isn't in C89, I guess one would have to
pick a window size by some other means for such an implementation.
Also at
http://codepad.org/D4JqjCNf
/**
* Trim whitespace on the left and right of a string
*/
#include <stdlib.h>
#include <ctype.h>
#include <stdint.h>
/* Return a pointer to the terminator for the trimmed string */
static char *trim_unsafe(char *string) {
#define TRIM_WINDOW_SIZE SIZE_MAX
char *right = string;
char c;
char *left = string;
size_t window = 0;
int found_left = 0;
while (*right) {
c = left[window];
if (!isspace(c)) {
/* Do not search for the left trim-point */
found_left = 1;
if (c)
/* Note a possible right trim-point */
right = string + window + 1;
}
if (!found_left) {
/* Search for the left trim-point */
++left;
continue;
}
if (!c || window == TRIM_WINDOW_SIZE) {
/* Prevent wasted effort */
if (left != string)
memmove(string, left, window);
/* Advance source and destination windows */
string += window;
left += window;
window = 0;
} else
++window;
if (!c)
/* Trim the right and we're done */
*right = 0;
}
/* Return a pointer to the terminator */
return right;
#undef TRIM_WINDOW_SIZE
}
char *trim(char *string) {
return string ? trim_unsafe(string) : string;
}
/**
* Testing trim function
*/
#include <stdio.h>
/* Handy for arrays */
#define NUM_OF_ELEMENTS(array_) \
(sizeof (array_) / sizeof *(array_))
#define FOR_EACH_ELEMENT(index_, array_) \
for ((index_) = 0; (index_) < NUM_OF_ELEMENTS(array_); (index_)++)
int main(void) {
char *tests[] = {
"",
" ",
" ",
"f",
" f",
" f",
" f ",
" f ",
"f ",
"f ",
"foo bar baz",
" foo bar baz",
" foo bar baz",
" foo bar baz ",
" foo bar baz ",
"foo bar baz ",
"foo bar baz "
};
int i;
char *cp;
FOR_EACH_ELEMENT(i, tests) {
char buf[80];
strcpy(buf, tests
);
printf("BEFORE: \"%s\"\n", buf);
cp = trim(buf);
printf(" AFTER: \"%s\"\nTERMINATOR: %p\n\n", buf, cp);
}
/* printf("%p\n", trim(NULL)); */
return 0;
}