如何以标准方式修剪前导/尾随空格?

时间:2008-09-23 17:57:15

标签: c string whitespace trim

是否有一种干净的,最好是标准方法来修剪C中字符串的前导和尾随空格?我会自己动手,但我认为这是一个同样常见的解决方案的常见问题。

40 个答案:

答案 0 :(得分:151)

如果你可以修改字符串:

// Note: This function returns a pointer to a substring of the original string.
// If the given string was allocated dynamically, the caller must not overwrite
// that pointer with the returned value, since the original pointer must be
// deallocated using the same allocator with which it was allocated.  The return
// value must NOT be deallocated using free() etc.
char *trimwhitespace(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;
}

如果你不能修改字符串,那么你可以使用基本相同的方法:

// Stores the trimmed input string into the given output buffer, which must be
// large enough to store the result.  If it is too small, the output is
// truncated.
size_t trimwhitespace(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;
}

答案 1 :(得分:31)

这是将字符串移动到缓冲区的第一个位置的一个。您可能希望这种行为,以便如果您动态分配字符串,您仍然可以在trim()返回的同一指针上释放它:

char *trim(char *str)
{
    size_t len = 0;
    char *frontp = str;
    char *endp = NULL;

    if( str == NULL ) { return NULL; }
    if( str[0] == '\0' ) { return str; }

    len = strlen(str);
    endp = str + len;

    /* Move the front and back pointers to address the first non-whitespace
     * characters from each end.
     */
    while( isspace((unsigned char) *frontp) ) { ++frontp; }
    if( endp != frontp )
    {
        while( isspace((unsigned char) *(--endp)) && endp != frontp ) {}
    }

    if( str + len - 1 != endp )
            *(endp + 1) = '\0';
    else if( frontp != str &&  endp == frontp )
            *str = '\0';

    /* Shift the string so that it starts at str so that if it's dynamically
     * allocated, we can still free it on the returned pointer.  Note the reuse
     * of endp to mean the front of the string buffer now.
     */
    endp = str;
    if( frontp != str )
    {
            while( *frontp ) { *endp++ = *frontp++; }
            *endp = '\0';
    }


    return str;
}

测试正确性:

int main(int argc, char *argv[])
{
    char *sample_strings[] =
    {
            "nothing to trim",
            "    trim the front",
            "trim the back     ",
            " trim one char front and back ",
            " trim one char front",
            "trim one char back ",
            "                   ",
            " ",
            "a",
            "",
            NULL
    };
    char test_buffer[64];
    int index;

    for( index = 0; sample_strings[index] != NULL; ++index )
    {
            strcpy( test_buffer, sample_strings[index] );
            printf("[%s] -> [%s]\n", sample_strings[index],
                                     trim(test_buffer));
    }

    /* The test prints the following:
    [nothing to trim] -> [nothing to trim]
    [    trim the front] -> [trim the front]
    [trim the back     ] -> [trim the back]
    [ trim one char front and back ] -> [trim one char front and back]
    [ trim one char front] -> [trim one char front]
    [trim one char back ] -> [trim one char back]
    [                   ] -> []
    [ ] -> []
    [a] -> [a]
    [] -> []
    */

    return 0;
}

源文件是trim.c.用'cc trim.c -o trim'编译。

答案 2 :(得分:21)

我的解决方案。字符串必须是可更改的。一些其他解决方案的优势在于它将非空间部分移动到开头,因此您可以继续使用旧指针,以防您以后必须释放它()。

void trim(char * s) {
    char * p = s;
    int l = strlen(p);

    while(isspace(p[l - 1])) p[--l] = 0;
    while(* p && isspace(* p)) ++p, --l;

    memmove(s, p, l + 1);
}   

此版本使用strndup()创建字符串的副本,而不是在适当位置编辑它。 strndup()需要_GNU_SOURCE,所以也许你需要使用malloc()和strncpy()创建自己的strndup()。

char * trim(char * s) {
    int l = strlen(s);

    while(isspace(s[l - 1])) --l;
    while(* s && isspace(* s)) ++s, --l;

    return strndup(s, l);
}

答案 3 :(得分:9)

这是我的C迷你库,用于修剪左,右,两者,全部,就地和分离,并修剪一组指定的字符(或默认为空格)。

strlib.h的内容:

#ifndef STRLIB_H_
#define STRLIB_H_ 1
enum strtrim_mode_t {
    STRLIB_MODE_ALL       = 0, 
    STRLIB_MODE_RIGHT     = 0x01, 
    STRLIB_MODE_LEFT      = 0x02, 
    STRLIB_MODE_BOTH      = 0x03
};

char *strcpytrim(char *d, // destination
                 char *s, // source
                 int mode,
                 char *delim
                 );

char *strtriml(char *d, char *s);
char *strtrimr(char *d, char *s);
char *strtrim(char *d, char *s); 
char *strkill(char *d, char *s);

char *triml(char *s);
char *trimr(char *s);
char *trim(char *s);
char *kill(char *s);
#endif

strlib.c的内容:

#include <strlib.h>

char *strcpytrim(char *d, // destination
                 char *s, // source
                 int mode,
                 char *delim
                 ) {
    char *o = d; // save orig
    char *e = 0; // end space ptr.
    char dtab[256] = {0};
    if (!s || !d) return 0;

    if (!delim) delim = " \t\n\f";
    while (*delim) 
        dtab[*delim++] = 1;

    while ( (*d = *s++) != 0 ) { 
        if (!dtab[0xFF & (unsigned int)*d]) { // Not a match char
            e = 0;       // Reset end pointer
        } else {
            if (!e) e = d;  // Found first match.

            if ( mode == STRLIB_MODE_ALL || ((mode != STRLIB_MODE_RIGHT) && (d == o)) ) 
                continue;
        }
        d++;
    }
    if (mode != STRLIB_MODE_LEFT && e) { // for everything but trim_left, delete trailing matches.
        *e = 0;
    }
    return o;
}

// perhaps these could be inlined in strlib.h
char *strtriml(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_LEFT, 0); }
char *strtrimr(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_RIGHT, 0); }
char *strtrim(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_BOTH, 0); }
char *strkill(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_ALL, 0); }

char *triml(char *s) { return strcpytrim(s, s, STRLIB_MODE_LEFT, 0); }
char *trimr(char *s) { return strcpytrim(s, s, STRLIB_MODE_RIGHT, 0); }
char *trim(char *s) { return strcpytrim(s, s, STRLIB_MODE_BOTH, 0); }
char *kill(char *s) { return strcpytrim(s, s, STRLIB_MODE_ALL, 0); }

一个主要例程就是这一切。 如果 src == dst ,它会修剪到位,否则, 它的工作方式与strcpy例程类似。 它修剪字符串 delim 中指定的一组字符,如果为null则修剪为空格。 它修剪左,右,两个和所有(如tr)。 没有多少,它只在字符串上迭代一次。有些人可能会抱怨左边的修剪右边开始,但是,无论如何都不需要从左边开始的修剪。 (不管怎样,你必须到达字符串的末尾才能进行正确的修剪,所以你可以随心所欲地完成工作。)可能会有关于流水线和缓存大小的争论等等 - 谁知道。由于解决方案从左到右工作并且只迭代一次,因此它也可以扩展为在流上工作。限制: unicode 字符串有效。

答案 4 :(得分:7)

这是我尝试使用简单但正确的就地修剪功能。

void trim(char *str)
{
    int i;
    int begin = 0;
    int end = strlen(str) - 1;

    while (isspace((unsigned char) str[begin]))
        begin++;

    while ((end >= begin) && isspace((unsigned char) str[end]))
        end--;

    // Shift all characters back to the start of the string array.
    for (i = begin; i <= end; i++)
        str[i - begin] = str[i];

    str[i - begin] = '\0'; // Null terminate string.
}

答案 5 :(得分:5)

装饰派对迟到了

特点:
 1.像其他一些答案一样,快速修剪开头。
 2.结束后,每个循环只进行1次测试即可修剪右边。像@ jfm3,但适用于所有白色空格字符串)
 3.为了避免char为签名char时未定义的行为,请将*s强制转换为unsigned char

  

字符处理“在所有情况下,参数都是int,其值应表示为unsigned char或等于宏的值EOF 1}}。如果参数有任何其他值,则行为未定义。“ C11§7.41

#include <ctype.h>

// Return a pointer to the trimmed string
char *string_trim_inplace(char *s) {
  while (isspace((unsigned char) *s)) s++;
  if (*s) {
    char *p = s;
    while (*p) p++;
    while (isspace((unsigned char) *(--p)));
    p[1] = '\0';
  }

  // If desire to shift the trimmed string

  return s;
}

@chqrlie评论说上面没有移动修剪后的字符串。这样做....

// Return a pointer to the (shifted) trimmed string
char *string_trim_inplace(char *s) {
  char *original = s;
  size_t len = 0;

  while (isspace((unsigned char) *s)) {
    s++;
  } 
  if (*s) {
    char *p = s;
    while (*p) p++;
    while (isspace((unsigned char) *(--p)));
    p[1] = '\0';
    len = (size_t) (p - s);
  }

  return (s == original) ? s : memove(original, s, len + 1);
}

答案 6 :(得分:4)

另一个,有一条线做真正的工作:

#include <stdio.h>

int main()
{
   const char *target = "   haha   ";
   char buf[256];
   sscanf(target, "%s", buf); // Trimming on both sides occurs here
   printf("<%s>\n", buf);
}

答案 7 :(得分:3)

我不喜欢这些答案中的大多数,因为他们做了以下一个或多个......

  1. 在原始指针的字符串中返回一个不同的指针(将两个不同指针同时用于同一事物的痛苦)。
  2. 无偿使用预先迭代整个字符串的 strlen()等内容。
  3. 使用非便携式操作系统特定的lib函数。
  4. Backscanned。
  5. &#39;进行比较&#39; 而不是 isspace(),以便保留TAB / CR / LF。
  6. 使用大型静态缓冲区浪费内存。
  7. 浪费了具有高成本功能的循环,例如 sscanf / sprintf
  8. 这是我的版本:

    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';
    }
    

答案 8 :(得分:3)

这是一个类似于@adam-rosenfields就地修改例程的解决方案,但没有不必要地使用strlen()。与@jkramer一样,字符串在缓冲区内左调整,因此您可以释放相同的指针。对于大字符串不是最佳的,因为它不使用memmove。包括@jfm3提到的++ / - 运算符。基于FCTX的单元测试包括在内。

#include <ctype.h>

void trim(char * const a)
{
    char *p = a, *q = a;
    while (isspace(*q))            ++q;
    while (*q)                     *p++ = *q++;
    *p = '\0';
    while (p > a && isspace(*--p)) *p = '\0';
}

/* See http://fctx.wildbearsoftware.com/ */
#include "fct.h"

FCT_BGN()
{
    FCT_QTEST_BGN(trim)
    {
        { char s[] = "";      trim(s); fct_chk_eq_str("",    s); } // Trivial
        { char s[] = "   ";   trim(s); fct_chk_eq_str("",    s); } // Trivial
        { char s[] = "\t";    trim(s); fct_chk_eq_str("",    s); } // Trivial
        { char s[] = "a";     trim(s); fct_chk_eq_str("a",   s); } // NOP
        { char s[] = "abc";   trim(s); fct_chk_eq_str("abc", s); } // NOP
        { char s[] = "  a";   trim(s); fct_chk_eq_str("a",   s); } // Leading
        { char s[] = "  a c"; trim(s); fct_chk_eq_str("a c", s); } // Leading
        { char s[] = "a  ";   trim(s); fct_chk_eq_str("a",   s); } // Trailing
        { char s[] = "a c  "; trim(s); fct_chk_eq_str("a c", s); } // Trailing
        { char s[] = " a ";   trim(s); fct_chk_eq_str("a",   s); } // Both
        { char s[] = " a c "; trim(s); fct_chk_eq_str("a c", s); } // Both

        // Villemoes pointed out an edge case that corrupted memory.  Thank you.
        // http://stackoverflow.com/questions/122616/#comment23332594_4505533
        {
          char s[] = "a     ";       // Buffer with whitespace before s + 2
          trim(s + 2);               // Trim "    " containing only whitespace
          fct_chk_eq_str("", s + 2); // Ensure correct result from the trim
          fct_chk_eq_str("a ", s);   // Ensure preceding buffer not mutated
        }

        // doukremt suggested I investigate this test case but
        // did not indicate the specific behavior that was objectionable.
        // http://stackoverflow.com/posts/comments/33571430
        {
          char s[] = "         foobar";  // Shifted across whitespace
          trim(s);                       // Trim
          fct_chk_eq_str("foobar", s);   // Leading string is correct

          // Here is what the algorithm produces:
          char r[16] = { 'f', 'o', 'o', 'b', 'a', 'r', '\0', ' ',                     
                         ' ', 'f', 'o', 'o', 'b', 'a', 'r', '\0'};
          fct_chk_eq_int(0, memcmp(s, r, sizeof(s)));
        }
    }
    FCT_QTEST_END();
}
FCT_END();

答案 9 :(得分:2)

跳过字符串中前导空格的最简单方法是,imho,

#include <stdio.h>

int main()
{
char *foo="     teststring      ";
char *bar;
sscanf(foo,"%s",bar);
printf("String is >%s<\n",bar);
    return 0;
}

答案 10 :(得分:2)

晚会很晚...

没有回溯的单遍正向扫描解决方案。源字符串中的每个字符都经过一次精确测试两次。 (因此,它应该比此处的大多数其他解决方案要快,尤其是在源字符串包含大量尾随空格的情况下。)

这包括两种解决方案,一种是将源字符串复制并修剪为另一个目标字符串,另一种是将源字符串修剪为适当位置。这两个函数使用相同的代码。

(可修改的)字符串就地移动,因此指向它的原始指针保持不变。

#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);
}

答案 11 :(得分:1)

如果您正在使用glib,那么您可以使用g_strstrip

答案 12 :(得分:1)

我知道有很多答案,但我在这里发布我的答案,看看我的解决方案是否足够好。

// Trims leading whitespace chars in left `str`, then copy at almost `n - 1` chars
// into the `out` buffer in which copying might stop when the first '\0' occurs, 
// and finally append '\0' to the position of the last non-trailing whitespace char.
// Reture the length the trimed string which '\0' is not count in like strlen().
size_t trim(char *out, size_t n, const char *str)
{
    // do nothing
    if(n == 0) return 0;    

    // ptr stop at the first non-leading space char
    while(isspace(*str)) str++;    

    if(*str == '\0') {
        out[0] = '\0';
        return 0;
    }    

    size_t i = 0;    

    // copy char to out until '\0' or i == n - 1
    for(i = 0; i < n - 1 && *str != '\0'; i++){
        out[i] = *str++;
    }    

    // deal with the trailing space
    while(isspace(out[--i]));    

    out[++i] = '\0';
    return i;
}

答案 13 :(得分:1)

为了保持这种增长,还有一个带有可修改字符串的选项:

void trimString(char *string)
{
    size_t i = 0, j = strlen(string);
    while (j > 0 && isspace((unsigned char)string[j - 1])) string[--j] = '\0';
    while (isspace((unsigned char)string[i])) i++;
    if (i > 0) memmove(string, string + i, j - i + 1);
}

答案 14 :(得分:1)

好,这是我对这个问题的看法。我相信这是最简洁的解决方案,可以修改字符串(free可以使用)并避免使用任何UB。对于较小的字符串,它可能比涉及memmove的解决方案要快。

void stripWS_LT(char *str)
{
    char *a = str, *b = str;
    while (isspace((unsigned char)*a)) a++;
    while (*b = *a++)  b++;
    while (b > str && isspace((unsigned char)*--b)) *b = 0;
}

答案 15 :(得分:1)

s非常有帮助,我想说我很高兴这篇文章可用,并展示我能够用这些例子做些什么。我需要标记一个更大的字符串,然后获取子字符串并找到最后一个 - 所以我可以从fgets()调用中删除换行符,并从该标记的前面删除空格 - 所以我可以很容易将它与静态字符串进行比较。上面帖子中的第一个例子让我在那里,谢谢。以下是我使用代码示例和输出的方法。

int _tmain(int argc, _TCHAR* argv[])
{
   FILE * fp;   // test file
   char currDBSStatstr[100] = {"/0"};
   char *beg;
   char *end;
   char *str1;
   char str[] = "Initializing DBS Configuration";
   fp = fopen("file2-1.txt","r");
   if (fp != NULL)
   {
      printf("File exists.\n");
      fgets(currDBSStatstr, sizeof(currDBSStatstr), fp);
   }
   else
   {
      printf("Error.\n");
      exit(2);
   }  
   //print string
   printf("String: %s\n", currDBSStatstr);
   //extract first string
   str1 = strtok(currDBSStatstr, ":-");
   //print first token
   printf("%s\n", str1);
   //get more tokens in sequence
   while(1)
   {
      //extract more tokens in sequence
      str1 = strtok(NULL, ":-");
      //check to see if done
      if (str1 == NULL)
      {
         printf("Tokenizing Done.\n");
         exit(0);
      }
      //print string after tokenizing Done
      printf("%s\n", str1);
      end = str1 + strlen(str1) - 1;
      while((end > str1) && (*end == '\n'))
      {
         end--;
         *(end+1) = 0;
         beg = str1;
         while(isspace(*str1))
            str1++;
      }
      printf("%s\n", str1);
      if (strcmp(str, str1) == 0)
         printf("Strings are equal.\n");
   }
   return 0;

}

<强>输出

  

档案存在。

     

字符串:DBS状态:DBS启动 - 初始化DBS配置

     

DBS State

     

DBS Startup

     

DBS Startup

     

初始化DBS配置

     

初始化DBS配置

     

字符串相等。

     

Tokenizing Done。

答案 16 :(得分:1)

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

char *trim_space(char *in)
{
    char *out = NULL;
    int len;
    if (in) {
        len = strlen(in);
        while(len && isspace(in[len - 1])) --len;
        while(len && *in && isspace(*in)) ++in, --len;
        if (len) {
            out = strndup(in, len);
        }
    }
    return out;
}

isspace有助于修剪所有空白。

  • 运行第一个循环以从最后一个字节开始检查空格字符并减小长度变量
  • 运行第二个循环,从第一个字节开始检查空格字符,并减少长度变量并增加char指针。
  • 最后,如果length变量大于0,则使用strndup通过排除空格来创建新的字符串缓冲区。

答案 17 :(得分:1)

#include "stdafx.h"
#include "malloc.h"
#include "string.h"

int main(int argc, char* argv[])
{

  char *ptr = (char*)malloc(sizeof(char)*30);
  strcpy(ptr,"            Hel  lo    wo           rl   d G    eo rocks!!!    by shahil    sucks b i          g       tim           e");

  int i = 0, j = 0;

  while(ptr[j]!='\0')
  {

      if(ptr[j] == ' ' )
      {
          j++;
          ptr[i] = ptr[j];
      }
      else
      {
          i++;
          j++;
          ptr[i] = ptr[j];
      }
  }


  printf("\noutput-%s\n",ptr);
        return 0;
}

答案 18 :(得分:1)

使用string library,例如:

Ustr *s1 = USTR1(\7, " 12345 ");

ustr_sc_trim_cstr(&s1, " ");
assert(ustr_cmp_cstr_eq(s1, "12345"));

...正如你所说这是一个“常见”问题,是的,你需要包含一个#include左右它并没有包含在libc中但是不要发明你自己的黑客作业存储随机指针和size_t的方式只会导致缓冲区溢出。

答案 19 :(得分:1)

我不确定你认为“无痛”。

C字符串非常痛苦。我们可以通过以下方式找到第一个非空白字符位置:

while (isspace(* p)) p++;

我们可以通过两个相似的小动作找到最后一个非空白字符位置:

while (* q) q++;
do { q--; } while (isspace(* q));

(我已经免除了同时使用*++运营商的痛苦。)

现在的问题是你对此有何看法?手头的数据类型实际上并不是一个容易思考的强大的抽象String,而是实际上只是一个存储字节数组。缺乏强大的数据类型,不可能编写与PHperytonby的chomp函数相同的函数。 C中的这样一个函数会返回什么?

答案 20 :(得分:0)

char* strtrim(char* const str)
{
    if (str != nullptr)
    {
        char const* begin{ str };
        while (std::isspace(*begin))
        {
            ++begin;
        }

        auto end{ begin };
        auto scout{ begin };
        while (*scout != '\0')
        {
            if (!std::isspace(*scout++))
            {
                end = scout;
            }
        }

        auto /* std::ptrdiff_t */ const length{ end - begin };
        if (begin != str)
        {
            std::memmove(str, begin, length);
        }

        str[length] = '\0';
    }

    return str;
}

答案 21 :(得分:0)

我是这样做的。它修剪了字符串,因此不必担心释放返回的字符串或丢失指向已分配字符串的指针。它可能不是最短的答案,但大多数读者都应该清楚。

#include <ctype.h>
#include <string.h>
void trim_str(char *s)
{
    const size_t s_len = strlen(s);

    int i;
    for (i = 0; i < s_len; i++)
    {
        if (!isspace( (unsigned char) s[i] )) break;
    }

    if (i == s_len)
    {
        // s is an empty string or contains only space characters

        s[0] = '\0';
    }
    else
    {
        // s contains non-space characters

        const char *non_space_beginning = s + i;

        char *non_space_ending = s + s_len - 1;
        while ( isspace( (unsigned char) *non_space_ending ) ) non_space_ending--;

        size_t trimmed_s_len = non_space_ending - non_space_beginning + 1;

        if (s != non_space_beginning)
        {
            // Non-space characters exist in the beginning of s

            memmove(s, non_space_beginning, trimmed_s_len);
        }

        s[trimmed_s_len] = '\0';
    }
}

答案 22 :(得分:0)

这个简短而简单,使用for循环,并且不会覆盖字符串边界。 您可以根据需要用isspace()替换测试。

void trim (char *s)         // trim leading and trailing spaces+tabs
{
 int i,j,k, len;

 j=k=0;
 len = strlen(s);
                    // find start of string
 for (i=0; i<len; i++) if ((s[i]!=32) && (s[i]!=9)) { j=i; break; }
                    // find end of string+1
 for (i=len-1; i>=j; i--) if ((s[i]!=32) && (s[i]!=9)) { k=i+1; break;} 

 if (k<=j) {s[0]=0; return;}        // all whitespace (j==k==0)

 len=k-j;
 for (i=0; i<len; i++) s[i] = s[j++];   // shift result to start of string
 s[i]=0;                // end the string

}//_trim

答案 23 :(得分:0)

由于其他答案似乎并没有直接改变字符串指针,而是依靠返回值,我想我会提供这种方法,该方法另外不使用任何库,因此适用于操作系统风格的编程:

// only used for printf in main
#include <stdio.h>

// note the char ** means we can modify the address
char *trimws(char **strp) { 
  char *str;
  // go to the end of the string
  for (str = *strp; *str; str++) 
    ;
  // back up one from the null terminator
  str--; 
  // set trailing ws to null
  for (; *str == ' '; str--) 
    *str = 0;
  // increment past leading ws
  for (str = *strp; *str == ' '; str++) 
    ;
  // set new begin address of string
  *strp = str; 
}

int main(void) {
  char buf[256] = "   whitespace    ";
  // pointer must be modifiable lvalue so we make bufp
  char **bufp = &buf;
  // pass in the address
  trimws(&bufp);
  // prints : XXXwhitespaceXXX
  printf("XXX%sXXX\n", bufp); 
  return 0;
}

答案 24 :(得分:0)

这里我使用动态内存分配将输入字符串修剪为函数trimStr。首先,我们发现输入字符串中存在多少个非空字符。然后,我们分配一个具有该大小的字符数组并处理空终止字符。当我们使用这个函数时,我们需要释放main函数内部的内存。

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

char *trimStr(char *str){
char *tmp = str;
printf("input string %s\n",str);
int nc = 0;

while(*tmp!='\0'){
  if (*tmp != ' '){
  nc++;
 }
 tmp++;
}
printf("total nonempty characters are %d\n",nc);
char *trim = NULL;

trim = malloc(sizeof(char)*(nc+1));
if (trim == NULL) return NULL;
tmp = str;
int ne = 0;

while(*tmp!='\0'){
  if (*tmp != ' '){
     trim[ne] = *tmp;
   ne++;
 }
 tmp++;
}
trim[nc] = '\0';

printf("trimmed string is %s\n",trim);

return trim; 
 }


int main(void){

char str[] = " s ta ck ove r fl o w  ";

char *trim = trimStr(str);

if (trim != NULL )free(trim);

return 0;
}

答案 25 :(得分:0)

IMO,它可以在没有 strlenisspace 的情况下完成。

char *
trim (char * s, char c)
{
    unsigned o = 0;
    char * sb = s;

    for (; *s == c; s++)
        o++;

    for (; *s != '\0'; s++)
        continue;
    for (; s - o > sb && *--s == c;)
        continue;

    if (o > 0)
        memmove(sb, sb + o, s + 1 - o - sb);
    if (*s != '\0')
        *(s + 1 - o) = '\0';

    return sb;
}

答案 26 :(得分:0)

就个人而言,我会自己动手。你可以使用strtok,但你需要注意这样做(特别是如果你要删除主角)你知道什么是内存。

摆脱尾随空格很简单,也很安全,因为你可以在最后一个空格的顶部加一个0,从最后算起。摆脱领先的空间意味着搬家。如果你想在适当的位置(可能是明智的)你可以继续将所有东西都移回一个角色,直到没有领先的空间。或者,为了提高效率,您可以找到第一个非空格字符的索引,并将所有内容移回该数字。或者,您可以使用指向第一个非空格字符的指针(但是您需要小心,就像使用strtok一样)。

答案 27 :(得分:0)

要从两边修剪我的琴弦,我会使用老人但是gooody;) 它可以修剪ascii小于空格的任何东西,这意味着控制字符也将被修剪!

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;
}

答案 28 :(得分:0)

您对使用标头Shlwapi.h中定义的StrTrim函数有何看法?它是直接的,而不是自己定义。
详情请见:
 http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx

如果你有 char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
这将ausCaptain"GeorgeBailey"而不是"GeorgeBailey "

答案 29 :(得分:0)

这些函数会修改原始缓冲区,因此如果动态分配,则为原始缓冲区 指针可以被释放。

#include <string.h>

void rstrip(char *string)
{
  int l;
  if (!string)
    return;
  l = strlen(string) - 1;
  while (isspace(string[l]) && l >= 0)
    string[l--] = 0;
}

void lstrip(char *string)
{
  int i, l;
  if (!string)
    return;
  l = strlen(string);
  while (isspace(string[(i = 0)]))
    while(i++ < l)
      string[i-1] = string[i];
}

void strip(char *string)
{
  lstrip(string);
  rstrip(string);
}

答案 30 :(得分:0)

这是我能想到的最短的实施方式:

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
}

答案 31 :(得分:0)

到目前为止,大多数答案都会执行以下操作之一:

  1. 在字符串末尾的Backtrack(即找到字符串的结尾,然后向后搜索,直到找到非空格字符)或
  2. 首先调用strlen(),然后再通过整个字符串。
  3. 此版本只进行一次传递,不会回溯。因此它可能比其他的更好,但前提是有几百个尾随空格(这在处理SQL查询的输出时并不罕见。)

    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';
    }
    

答案 32 :(得分:0)

比赛有点晚了,但我会将我的惯例投入到战斗中。它们可能不是最有效率的,但我相信它们是正确的并且它们很简单(rtrim()推动复杂性包络):

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

/*
    Public domain implementations of in-place string trim functions

    Michael Burr
    michael.burr@nth-element.com
    2010
*/

char* ltrim(char* s) 
{
    char* newstart = s;

    while (isspace( *newstart)) {
        ++newstart;
    }

    // newstart points to first non-whitespace char (which might be '\0')
    memmove( s, newstart, strlen( newstart) + 1); // don't forget to move the '\0' terminator

    return s;
}


char* rtrim( char* s)
{
    char* end = s + strlen( s);

    // find the last non-whitespace character
    while ((end != s) && isspace( *(end-1))) {
            --end;
    }

    // at this point either (end == s) and s is either empty or all whitespace
    //      so it needs to be made empty, or
    //      end points just past the last non-whitespace character (it might point
    //      at the '\0' terminator, in which case there's no problem writing
    //      another there).    
    *end = '\0';

    return s;
}

char*  trim( char* s)
{
    return rtrim( ltrim( s));
}

答案 33 :(得分:0)

我只包含代码,因为到目前为止发布的代码似乎不是最理想的(我还没有代表发表评论。)

void inplace_trim(char* s)
{
    int start, end = strlen(s);
    for (start = 0; isspace(s[start]); ++start) {}
    if (s[start]) {
        while (end > 0 && isspace(s[end-1]))
            --end;
        memmove(s, &s[start], end - start);
    }
    s[end - start] = '\0';
}

char* copy_trim(const char* s)
{
    int start, end;
    for (start = 0; isspace(s[start]); ++start) {}
    for (end = strlen(s); end > 0 && isspace(s[end-1]); --end) {}
    return strndup(s + start, end - start);
}

strndup()是GNU扩展。如果你没有它或类似的东西,请自己动手。例如:

r = strdup(s + start);
r[end-start] = '\0';

答案 34 :(得分:-1)

void trim(char* string) {
    int lenght = strlen(string);
    int i=0;

    while(string[0] ==' ') {
        for(i=0; i<lenght; i++) {
            string[i] = string[i+1];
        }
        lenght--;
    }


    for(i=lenght-1; i>0; i--) {
        if(string[i] == ' ') {
            string[i] = '\0';
        } else {
            break;
        }
    }
}

答案 35 :(得分:-1)

void trim(char* const str)
{
    char* begin = str;
    char* end = str;
    while (isspace(*begin))
    {
        ++begin;
    }
    char* s = begin;
    while (*s != '\0')
    {
        if (!isspace(*s++))
        {
            end = s;
        }
    }
    *end = '\0';
    const int dist = end - begin;
    if (begin > str && dist > 0)
    {
        memmove(str, begin, dist + 1);
    }
}

修改字符串,因此您仍然可以删除它。

不使用花式裤子库功能(除非你考虑memmove花哨)。

处理字符串重叠。

修剪正面和背面(不是中间,抱歉)。

如果字符串很大则快速(memmove通常用汇编语言编写)。

只在需要时移动字符(我在大多数用例中都认为这是正确的,因为字符串很少有前导空格,并且通常不会有尾部空格)

我想测试一下,但我迟到了。喜欢发现错误...: - )

答案 36 :(得分:-1)

C ++ STL风格

char** buffer_out

http://ideone.com/VwJaq9

答案 37 :(得分:-1)

以下是我在Linux内核代码中披露的问题:

/**
 * skip_spaces - Removes leading whitespace from @s.
 * @s: The string to be stripped.
 *
 * Returns a pointer to the first non-whitespace character in @s.
 */
char *skip_spaces(const char *str)
{
    while (isspace(*str))
            ++str;
    return (char *)str;
}

/**
 * strim - Removes leading and trailing whitespace from @s.
 * @s: The string to be stripped.
 *
 * Note that the first trailing whitespace is replaced with a %NUL-terminator
 * in the given string @s. Returns a pointer to the first non-whitespace
 * character in @s.
 */
char *strim(char *s)
{
    size_t size;
    char *end;

    size = strlen(s);

    if (!size)
            return s;

    end = s + size - 1;
    while (end >= s && isspace(*end))
            end--;
    *(end + 1) = '\0';

    return skip_spaces(s);
}

由于原点,它应该是无bug的; - )

我认为我的一件事更接近KISS原则:

/**
 * trim spaces
 **/
char * trim_inplace(char * s, int len)
{
    // trim leading
    while (len && isspace(s[0]))
    {
        s++; len--;
    }

    // trim trailing
    while (len && isspace(s[len - 1]))
    {
        s[len - 1] = 0; len--;
    }

    return s;
}

答案 38 :(得分:-1)

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

main()
{
    char sent[10]={' ',' ',' ','s','t','a','r','s',' ',' '};
    int i,j=0;
    char rec[10];

    for(i=0;i<=10;i++)
    {
        if(!isspace(sent[i]))
        {

            rec[j]=sent[i];
            j++;
        }
    }

printf("\n%s\n",rec);

}

答案 39 :(得分:-1)

这是一个做你想要的功能。它应该处理字符串都是空白的退化情况。您必须传入输出缓冲区和缓冲区的长度,这意味着您必须传入您分配的缓冲区。

void str_trim(char *output, const char *text, int32 max_len)
{
    int32 i, j, length;
    length = strlen(text);

    if (max_len < 0) {
        max_len = length + 1;
    }

    for (i=0; i<length; i++) {
        if ( (text[i] != ' ') && (text[i] != '\t') && (text[i] != '\n') && (text[i] != '\r')) {
            break;
        }
    }

    if (i == length) {
        // handle lines that are all whitespace
        output[0] = 0;
        return;
    }

    for (j=length-1; j>=0; j--) {
        if ( (text[j] != ' ') && (text[j] != '\t') && (text[j] != '\n') && (text[j] != '\r')) {
            break;
        }
    }

    length = j + 1 - i;
    strncpy(output, text + i, length);
    output[length] = 0;
}

循环中的if语句可能会被 isspace(text [i]) isspace(text [j])替换,以使这些行更容易阅读。我认为我让他们设置了这种方式,因为有一些我不想测试的角色,但看起来我现在覆盖了所有的空白: - )