How do I trim leading/trailing whitespace in a sta

2018-12-31 05:19发布

Is there a clean, preferably standard method of trimming leading and trailing whitespace from a string in C? I'd roll my own, but I would think this is a common problem with an equally common solution.

30条回答
浅入江南
2楼-- · 2018-12-31 06:00

Most of the answers so far do one of the following:

  1. Backtrack at the end of the string (i.e. find the end of the string and then seek backwards until a non-space character is found,) or
  2. Call strlen() first, making a second pass through the whole string.

This version makes only one pass and does not backtrack. Hence it may perform better than the others, though only if it is common to have hundreds of trailing spaces (which is not unusual when dealing with the output of a SQL query.)

static char const WHITESPACE[] = " \t\n\r";

static void get_trim_bounds(char  const *s,
                            char const **firstWord,
                            char const **trailingSpace)
{
    char const *lastWord;
    *firstWord = lastWord = s + strspn(s, WHITESPACE);
    do
    {
        *trailingSpace = lastWord + strcspn(lastWord, WHITESPACE);
        lastWord = *trailingSpace + strspn(*trailingSpace, WHITESPACE);
    }
    while (*lastWord != '\0');
}

char *copy_trim(char const *s)
{
    char const *firstWord, *trailingSpace;
    char *result;
    size_t newLength;

    get_trim_bounds(s, &firstWord, &trailingSpace);
    newLength = trailingSpace - firstWord;

    result = malloc(newLength + 1);
    memcpy(result, firstWord, newLength);
    result[newLength] = '\0';
    return result;
}

void inplace_trim(char *s)
{
    char const *firstWord, *trailingSpace;
    size_t newLength;

    get_trim_bounds(s, &firstWord, &trailingSpace);
    newLength = trailingSpace - firstWord;

    memmove(s, firstWord, newLength);
    s[newLength] = '\0';
}
查看更多
几人难应
3楼-- · 2018-12-31 06:02

This is the shortest possible implementation I can think of:

static const char *WhiteSpace=" \n\r\t";
char* trim(char *t)
{
    char *e=t+(t!=NULL?strlen(t):0);               // *e initially points to end of string
    if (t==NULL) return;
    do --e; while (strchr(WhiteSpace, *e) && e>=t);  // Find last char that is not \r\n\t
    *(++e)=0;                                      // Null-terminate
    e=t+strspn (t,WhiteSpace);                           // Find first char that is not \t
    return e>t?memmove(t,e,strlen(e)+1):t;                  // memmove string contents and terminator
}
查看更多
何处买醉
4楼-- · 2018-12-31 06:03

I didn't like most of these answers because they did one or more of the following...

  1. Returned a different pointer inside the original pointer's string (kind of a pain to juggle two different pointers to the same thing).
  2. Made gratuitous use of things like strlen() that pre-iterate the entire string.
  3. Used non-portable OS-specific lib functions.
  4. Backscanned.
  5. Used comparison to ' ' instead of isspace() so that TAB / CR / LF are preserved.
  6. Wasted memory with large static buffers.
  7. Wasted cycles with high-cost functions like sscanf/sprintf.

Here is my version:

void fnStrTrimInPlace(char *szWrite) {

    const char *szWriteOrig = szWrite;
    char       *szLastSpace = szWrite, *szRead = szWrite;
    int        bNotSpace;

    // SHIFT STRING, STARTING AT FIRST NON-SPACE CHAR, LEFTMOST
    while( *szRead != '\0' ) {

        bNotSpace = !isspace((unsigned char)(*szRead));

        if( (szWrite != szWriteOrig) || bNotSpace ) {

            *szWrite = *szRead;
            szWrite++;

            // TRACK POINTER TO LAST NON-SPACE
            if( bNotSpace )
                szLastSpace = szWrite;
        }

        szRead++;
    }

    // TERMINATE AFTER LAST NON-SPACE (OR BEGINNING IF THERE WAS NO NON-SPACE)
    *szLastSpace = '\0';
}
查看更多
余生无你
5楼-- · 2018-12-31 06:03

Very late to the party...

Single-pass forward-scanning solution with no backtracking. Every character in the source string is tested exactly once. (So it should be faster than most of the other solutions here, especially if the source string has a lot of trailing spaces.)

This includes two solutions, one to copy and trim a source string into another destination string, and the other to trim the source string in place. Both functions use the same code.

The (modifiable) string is moved in-place, so the original pointer to it remains unchanged.

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

char * trim2(char *d, const char *s)
{
    // Sanity checks
    if (s == NULL  ||  d == NULL)
        return NULL;

    // Skip leading spaces        
    const unsigned char * p = (const unsigned char *)s;
    while (isspace(*p))
        p++;

    // Copy the string
    unsigned char * dst = (unsigned char *)d;   // d and s can be the same
    unsigned char * end = dst;
    while (*p != '\0')
    {
        if (!isspace(*dst++ = *p++))
            end = dst;
    }

    // Truncate trailing spaces
    *end = '\0';
    return d;
}

char * trim(char *s)
{
    return trim2(s, s);
}
查看更多
低头抚发
6楼-- · 2018-12-31 06:04

If you're using glib, then you can use g_strstrip

查看更多
梦醉为红颜
7楼-- · 2018-12-31 06:07

To trim my strings from the both sides I use the oldie but the gooody ;) It can trim anything with ascii less than a space, meaning that the control chars will be trimmed also !

char *trimAll(char *strData)
{
  unsigned int L = strlen(strData);
  if(L > 0){ L--; }else{ return strData; }
  size_t S = 0, E = L;
  while((!(strData[S] > ' ') || !(strData[E] > ' ')) && (S >= 0) && (S <= L) && (E >= 0) && (E <= L))
  {
    if(strData[S] <= ' '){ S++; }
    if(strData[E] <= ' '){ E--; }
  }
  if(S == 0 && E == L){ return strData; } // Nothing to be done
  if((S >= 0) && (S <= L) && (E >= 0) && (E <= L)){
    L = E - S + 1;
    memmove(strData,&strData[S],L); strData[L] = '\0';
  }else{ strData[0] = '\0'; }
  return strData;
}
查看更多
登录 后发表回答