views:

11053

answers:

8
+5  Q: 

Trim a string in C

Briefly:

I'm after the equivalent of .NET's String.Trim in C using the win32 and standard C api (compiling with MSVC2008 so I have access to all the C++ stuff if needed, but I am just trying to trim a char*).

Given that there is strchr, strtok, and all manner of other string functions, surely there should be a trim function, or one that can be repurposed...

Thanks

+6  A: 

You can use the standard isspace() function in ctype.h to achieve this. Simply compare the beginning and end characters of your character array until both ends no longer have spaces.

"spaces" include:

' ' (0x20) space (SPC)

'\t' (0x09) horizontal tab (TAB)

'\n' (0x0a) newline (LF)

'\v' (0x0b) vertical tab (VT)

'\f' (0x0c) feed (FF)

'\r' (0x0d) carriage return (CR)

although there is no function which will do all of the work for you, you will have to roll your own solution to compare each side of the given character array repeatedly until no spaces remain.

Edit:

Since you have access to C++, Boost has a trim implementation waiting for you to make your life a lot easier.

John T
I don't see what's wrong with this but ok, I've got big shoulders and a lot of reputation to spare :)
John T
I don't believe there is anything wrong with this. So +1 :)
Stephan202
+5  A: 

There is no standard library function to do this, but it's not too hard to roll your own. There is an existing question on SO about doing this that was answered with source code.

Andrew Grant
Thanks for that. I find it retarded that there's no library function (if everyone rolls their own then they're all going to mishandle unicode, etc in various different ways), but I guess it is what it is...
Orion Edwards
C's string handling is awkward, more so with Unicode. C++ patches it with std::string, but making strings natural requires a redesign. C, despite all its virtues, is far from being a perfect language.
David Thornley
Heh. C was invented in '72. Unicode didn't come along until the '90s. For most of C's history there was nothing but ASCII, and str[x]='\0'; would do the job just fine.
T.E.D.
A: 

Easiest thing to do is a simple loop. I'm going to assume that you want the trimmed string returned in place.

char *
strTrim(char * s){
    int ix, jx;
    int len ;
    char * buf 
    len = strlen(s);  /* possibly should use strnlen */
    buf = (char *) malloc(strlen(s)+1);

    for(ix=0, jx=0; ix < len; ix++){
       if(!isspace(s[ix]))
          buf[jx++] = s[ix];

    buf[jx] = '\0';
    strncpy(s, buf, jx);  /* always looks as far as the null, but who cares? */
    free(buf);            /* no good leak goes unpunished */
    return s;             /* modifies s in place *and* returns it for swank */
 }

This gets rid of embedded blanks too, if String.Trim doesn't then it needs a bit more logic.

Charlie Martin
It's hardly "modifying in place" if you malloc a new buffer and then copy it back in!
Andrew Grant
it's modified in place as far as the interface is concerned.
Charlie Martin
yeah, good luck selling that one in your next interview :)
Andrew Grant
Why so complex? Why not just loop from line to first non-empty character and replace spaces with 0? Very inefficient piece of code. That's Java way of coding, not a C one.
qrdl
@Andrew, I don't go to interviews, I give interviews. @qrdl, then you'd have an array of char that started with \0. This in C is called a "zero length string".
Charlie Martin
One of the truly fascinating things about having written C for 30 years is that there's always someone to tell me I don't know what I'm doing. They almost invariably mean *they* don't know what I'm doing.
Charlie Martin
You're right - I have no idea what you're doing using malloc for this :) Look, my point is that it's a poor implementation and certainly not 'in-place'. End of story. Smug replies just make you look like the subject of thedailywtf.com story. Let's move on.
Andrew Grant
Ah. That's easy. It's the only way to build this that (a) isn't sensitive to the size of the buffer and in particular doesn't impose a length limit on the input, (b) can be done in a single pass, and (c) doesn't return malloc'ed memory.
Charlie Martin
malloc prevents an in-place function from being sensitive to input-sizes? Sorry, that's just wrong. Calling strlen twice lost you the single-pass argument too. Despite being slower and longer your function doesn't actually even work - when does a trim function ever remove spaces between words?
Andrew Grant
You save the length of the string into `len` (which is the wrong kind of variable, it should be of type `size_t`), but rather than using `len` for the dynamic memory allocation, you just call `strlen` again. You also suggest the use of non-standard function `strnlen`. It removes "embedded blanks"?! You also have a syntax error (you're missing the closing brace for your `for` loop). You cast the return value from `malloc`, and you don't even check for `NULL`.
dreamlax
+2  A: 

Surprised to see such implementations. I usually do trim like this:

char *trim(char *s) {
    char *ptr;
    if (!s)
        return NULL;   // handle NULL string
    if (!*s)
        return s;      // handle empty string
    for (ptr = s + strlen(s) - 1; (ptr >= s) && isspace(*ptr); --ptr);
    ptr[1] = '\0';
    return s;
}

It is fast and reliable - serves me many years.

qrdl
Oh, i think that can cause a buffer underrun, consider this: char buffer[] = " "; trim(buffer); Then you at least reading buffer[-1], and if it is randomly a white space, you'll even write out side of your buffer.
quinmars
Nice one! I've added extra check for this. Will check my production code as well :)
qrdl
+1  A: 

This made me want to write my own - I didn't like the ones that had been provided. Seems to me there should be 3 functions.

char *ltrim(char *s)
{
    while(isspace(*s)) s++;
    return s;
}

char *rtrim(char *s)
{
    char* back = s + strlen(s);
    while(isspace(*--back));
    *(back+1) = '\0';
    return s;
}

char *trim(char *s)
{
    return rtrim(ltrim(s)); 
}
JRL
A: 
void inPlaceStrTrim(char* str) {
    int k = 0;
    int i = 0;
    for (i=0; str[i] != '\0';) {
        if (isspace(str[i])) {
            // we have got a space...
            k = i;
            for (int j=i; j<strlen(str)-1; j++) {
                str[j] = str[j+1];
            }
            str[strlen(str)-1] = '\0';
            i = k; // start the loop again where we ended..
        } else {
            i++;
        }
    }
}
Sudipto M
Ouch. That's nasty. It looks like it will also remove inner spaces? " foo bar " should trim to "foo bar", not "foobar"
Orion Edwards
A: 

How about this... It only requires one iteration over the string (doesn't use strlen, which iterates over the string). When the function returns you get a pointer to the start of the trimmed string which is null terminated. The string is trimmed of spaces from the left (until the first character is found). The string is also trimmed of all trailing spaces after the last nonspace character.

char* trim(char* input) {
    char* start = input;
    while (isSpace(*start)) { //trim left
        start++;
    }

    char* ptr = start;
    char* end = start;
    while (*ptr++ != '\0') { //trim right
        if (!isSpace(*ptr)) { //only move end pointer if char isn't a space
            end = ptr;
        }
    }

    *end = '\0'; //terminate the trimmed string with a null
    return start;
}

bool isSpace(char c) {
    switch (c) {
        case ' ':
        case '\n':
        case '\t':
        case '\f':
        case '\r':
            return true;
            break;
        default:
            return false;
            break;
    }
}
Nick Maludy
A: 
/* iMode 0:ALL, 1:Left, 2:Right*/
char* Trim(char* szStr,const char ch, int iMode)
{
    if (szStr == NULL)
        return NULL;
    char szTmp[1024*10] = { 0x00 };
    strcpy(szTmp, szStr);
    int iLen = strlen(szTmp);
    char* pStart = szTmp;
    char* pEnd = szTmp+iLen;
    int i;
    for(i = 0;i < iLen;i++){
        if (szTmp[i] == ch && pStart == szTmp+i && iMode != 2)
            ++pStart;
        if (szTmp[iLen-i-1] == ch && pEnd == szTmp+iLen-i && iMode != 1)
            *(--pEnd) = '\0';
    }
    strcpy(szStr, pStart);
    return szStr;
}
xiujie_cn