【问题标题】:Trim a string in C [duplicate]修剪C中的字符串[重复]
【发布时间】:2010-10-13 23:32:52
【问题描述】:

简述:

我正在使用 win32 和标准 C api 在 C 中使用相当于 .NET 的 String.Trim(使用 MSVC2008 编译,因此如果需要我可以访问所有 C++ 内容,但我只是想修剪 @987654322 @)。

鉴于有 strchr、strtok 和各种其他字符串函数,当然应该有一个修剪函数,或者可以重新调整用途的函数......

谢谢

【问题讨论】:

    标签: c winapi string


    【解决方案1】:

    不是最好的方法,但它有效

    char* Trim(char* str)
    {
        int len = strlen(str);
        char* buff = new char[len];
        int i = 0;
        memset(buff,0,len*sizeof(char));
        do{
            if(isspace(*str)) continue;
            buff[i] = *str; ++i;
        } while(*(++str) != '\0');
        return buff;
    }
    

    【讨论】:

      【解决方案2】:
      void ltrim(char str[PATH_MAX])
      {
              int i = 0, j = 0;
              char buf[PATH_MAX];
              strcpy(buf, str);
              for(;str[i] == ' ';i++);
      
              for(;str[i] != '\0';i++,j++)
                      buf[j] = str[i];
              buf[j] = '\0';
              strcpy(str, buf);
      }
      

      【讨论】:

        【解决方案3】:

        当返回值总是等于参数时,我喜欢它。这样,如果字符串数组已经分配了malloc(),它就可以安全地再次为free()。

        /* Remove leading whitespaces */
        char *ltrim(char *const s)
        {
                size_t len;
                char *cur;
        
                if(s && *s) {
                        len = strlen(s);
                        cur = s;
        
                        while(*cur && isspace(*cur))
                                ++cur, --len;
        
                        if(s != cur)
                                memmove(s, cur, len + 1);
        
                }
        
                return s;
        }
        
        /* Remove trailing whitespaces */
        char *rtrim(char *const s)
        {
                size_t len;
                char *cur;
        
                if(s && *s) {
                        len = strlen(s);
                        cur = s + len - 1;
        
                        while(cur != s && isspace(*cur))
                                --cur, --len;
        
                        cur[isspace(*cur) ? 0 : 1] = '\0';
                }
        
                return s;
        }
        
        /* Remove leading and trailing whitespaces */
        char *trim(char *const s)
        {
                rtrim(s);  // order matters
                ltrim(s);
        
                return s;
        }
        

        【讨论】:

          【解决方案4】:
          /* Function to remove white spaces on both sides of a string i.e trim */
          
          void trim (char *s)
          {
              int i;
          
              while (isspace (*s)) s++;   // skip left side white spaces
              for (i = strlen (s) - 1; (isspace (s[i])); i--) ;   // skip right side white spaces
              s[i + 1] = '\0';
              printf ("%s\n", s);
          }
          

          【讨论】:

            【解决方案5】:

            这是我的实现,其行为类似于 libc 中的内置字符串函数(也就是说,它需要一个 c 字符串,它对其进行修改并将其返回给调用者)。

            它修剪前导空格并将剩余的字符向左移动,因为它从左到右解析字符串。然后它标记字符串的新结尾并开始向后解析,用 '\0' 替换尾随空格,直到找到非空格字符或字符串的开头。我相信这些是这个特定任务的最小可能迭代。

            // ----------------------------------------------------------------------------
            // trim leading & trailing spaces from string s (return modified string s)
            // alg:
            // - skip leading spaces, via cp1
            // - shift remaining *cp1's to the left, via cp2
            // - mark a new end of string
            // - replace trailing spaces with '\0', via cp2
            // - return the trimmed s
            //
            char *s_trim(char *s)
            {
                char *cp1;                              // for parsing the whole s
                char *cp2;                              // for shifting & padding
            
                // skip leading spaces, shift remaining chars
                for (cp1=s; isspace(*cp1); cp1++ )      // skip leading spaces, via cp1
                    ;
                for (cp2=s; *cp1; cp1++, cp2++)         // shift left remaining chars, via cp2
                    *cp2 = *cp1;
                *cp2-- = 0;                             // mark new end of string for s
            
                // replace trailing spaces with '\0'
                while ( cp2 > s && isspace(*cp2) )
                    *cp2-- = 0;                         // pad with '\0's
            
                return s;
            }
            

            【讨论】:

              【解决方案6】:
              static inline void ut_trim(char * str) {
                 char * start = str;
                 char * end = start + strlen(str);
              
                 while (--end >= start) {   /* trim right */
                    if (!isspace(*end))
                       break;
                 }
                 *(++end) = '\0';
              
                 while (isspace(*start))    /* trim left */
                    start++;
              
                 if (start != str)          /* there is a string */
                    memmove(str, start, end - start + 1);
              }
              

              【讨论】:

                【解决方案7】:
                #include "stdafx.h"
                #include <string.h>
                #include <ctype.h>
                
                char* trim(char* input);
                
                
                int _tmain(int argc, _TCHAR* argv[])
                {
                    char sz1[]="  MQRFH  ";
                    char sz2[]=" MQRFH";
                    char sz3[]="  MQR FH";
                    char sz4[]="MQRFH  ";
                    char sz5[]="MQRFH";
                    char sz6[]="M";
                    char sz7[]="M ";
                    char sz8[]=" M";
                    char sz9[]="";
                    char sz10[]="        ";
                
                    printf("sz1:[%s] %d\n",trim(sz1), strlen(sz1));
                    printf("sz2:[%s] %d\n",trim(sz2), strlen(sz2));
                    printf("sz3:[%s] %d\n",trim(sz3), strlen(sz3));
                    printf("sz4:[%s] %d\n",trim(sz4), strlen(sz4));
                    printf("sz5:[%s] %d\n",trim(sz5), strlen(sz5));
                    printf("sz6:[%s] %d\n",trim(sz6), strlen(sz6));
                    printf("sz7:[%s] %d\n",trim(sz7), strlen(sz7));
                    printf("sz8:[%s] %d\n",trim(sz8), strlen(sz8));
                    printf("sz9:[%s] %d\n",trim(sz9), strlen(sz9));
                    printf("sz10:[%s] %d\n",trim(sz10), strlen(sz10));
                
                    return 0;
                }
                
                char *ltrim(char *s) 
                {     
                    while(isspace(*s)) s++;     
                    return s; 
                }  
                
                char *rtrim(char *s) 
                {     
                    char* back;
                    int len = strlen(s);
                
                    if(len == 0)
                        return(s); 
                
                    back = s + len;     
                    while(isspace(*--back));     
                    *(back+1) = '\0';     
                    return s; 
                }  
                
                char *trim(char *s) 
                {     
                    return rtrim(ltrim(s));  
                } 
                

                输出:

                sz1:[MQRFH] 9
                sz2:[MQRFH] 6
                sz3:[MQR FH] 8
                sz4:[MQRFH] 7
                sz5:[MQRFH] 5
                sz6:[M] 1
                sz7:[M] 2
                sz8:[M] 2
                sz9:[] 0
                sz10:[] 8
                

                【讨论】:

                  【解决方案8】:
                  /* 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;
                  }
                  

                  【讨论】:

                    【解决方案9】:

                    这个怎么样...它只需要对字符串进行一次迭代(不使用 strlen,它对字符串进行迭代)。当函数返回时,您将获得一个指向已修剪字符串开头的指针,该字符串以空值结尾。字符串从左边开始去掉空格(直到找到第一个字符)。最后一个非空格字符之后的所有尾随空格也会被修剪掉。

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

                    【讨论】:

                    • 这会就地修改输入字符串。虽然看起来性能会很好,但除非绝对必须,否则我不会使用它,因为就地修改字符串可能是出乎意料的,就像将开始指针返回到现有字符串的中间一样。调用者必须知道在不再需要“修剪”的字符串之前不要删除原始字符串
                    【解决方案10】:
                    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++;
                            }
                        }
                    }
                    

                    【讨论】:

                    • 哎哟。这很讨厌。看起来它也会删除内部空间? “ foo bar” 应该修剪为“foo bar”,而不是“foobar”
                    • 即使操作也要求修剪内部,这段代码也是非常多余的。太可怕了!
                    【解决方案11】:

                    这让我想自己写 - 我不喜欢提供的那些。在我看来应该有 3 个功能。

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

                    【讨论】:

                    • 我尝试了这个线程中提到的其他实现..它工作了一段时间,直到我突然遇到一个段错误..我花了很多时间尝试调试并发现它是间接的由修整代码引起...我尝试了您的版本并且段错误消失了:) 谢谢! (虽然我仍然不明白为什么会发生段错误)
                    • 编辑:审查我的代码的人建议我检查 NULL 输入值..也许这是您可能想要添加的内容
                    • ltrim 返回一个不适合传递给free 的指针。如果你使用它(或者,当然,trim),请确保你抓住了原始指针——尤其是在没有将s 存储在其他地方之前,不要尝试像s = trim(s); 这样的东西。
                    • 是什么阻止了 rtrim 越过字符串的开头?对我来说看起来不安全......
                    【解决方案12】:

                    很惊讶看到这样的实现。我通常这样修剪:

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

                    它快速可靠 - 为我服务多年。

                    【讨论】:

                    • 哦,我认为这会导致缓冲区欠载,考虑一下: char buffer[] = " ";修剪(缓冲区);然后你至少读取缓冲区[-1],如果它是随机的空白,你甚至会写出你的缓冲区。
                    • 不错的一个!我为此添加了额外的检查。也会检查我的生产代码:)
                    【解决方案13】:

                    您可以使用 ctype.h 中的标准 isspace() 函数来实现此目的。只需比较字符数组的开始和结束字符,直到两端不再有空格。

                    “空格”包括:

                    ''(0x20)空间(SPC)

                    '\t' (0x09) 水平制表符 (TAB)

                    '\n' (0x0a) 换行符 (LF)

                    '\v' (0x0b) 垂直制表符 (VT)

                    '\f' (0x0c) 提要 (FF)

                    '\r' (0x0d) 回车(CR)

                    虽然没有可以为您完成所有工作的函数,但您必须推出自己的解决方案来反复比较给定字符数组的每一侧,直到没有空格。

                    编辑:

                    由于您可以使用 C++,Boost 有 a trim implementation 等着您,让您的生活更轻松。

                    【讨论】:

                      【解决方案14】:

                      最简单的事情就是一个简单的循环。我将假设您希望将修剪后的字符串返回到位。

                      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 */
                       }
                      

                      这也消除了嵌入的空白,如果 String.Trim 没有,那么它需要更多的逻辑。

                      【讨论】:

                      • 如果你 malloc 一个新的缓冲区然后将其复制回来,这几乎不是“就地修改”!
                      • 就界面而言已经修改到位了。
                      • 是的,祝你在下一次面试中卖掉那个:)
                      • 为什么这么复杂?为什么不从行循环到第一个非空字符并用 0 替换空格?非常低效的一段代码。那是 Java 的编码方式,而不是 C 的。
                      • malloc 防止就地函数对输入大小敏感?对不起,那是错误的。调用 strlen 两次也失去了单通参数。尽管速度越来越慢,但您的功能实际上甚至不起作用 - 修剪功能何时会删除单词之间的空格?
                      【解决方案15】:

                      没有标准的库函数可以做到这一点,但自己动手并不难。 SO上有一个existing question关于这样做,并用源代码回答。

                      【讨论】:

                      • 谢谢。我发现没有库函数很迟钝(如果每个人都自己动手,那么他们都会以各种不同的方式错误处理 unicode 等),但我想它就是这样......
                      • C 的字符串处理很笨拙,Unicode 更是如此。 C++ 用 std::string 对其进行了修补,但使字符串自然需要重新设计。尽管 C 有很多优点,但它远非完美的语言。
                      • 嘿。 C 是在 72 年发明的。 Unicode 直到 90 年代才出现。在 C 的大部分历史中,只有 ASCII 和 str[x]='\0';会做得很好。
                      猜你喜欢
                      • 2014-03-26
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2018-12-25
                      • 2015-02-07
                      • 1970-01-01
                      相关资源
                      最近更新 更多