在C语言中如何截取字符串

29
简要说明:
我想找到C语言中使用win32和标准C API的等效于.NET的String.Trim的方法(使用MSVC2008编译,因此如果需要,可以访问所有C++内容,但我只是尝试修剪一个char*)。
鉴于存在strchrstrtok和各种其他字符串函数,肯定有一个修剪函数,或者可以重新用途的函数...
谢谢
15个回答

23

没有标准库函数可以实现此功能,但自己编写也不太难。在stackoverflow上有一个关于如何做到这一点的现有问题,其中已经提供了源代码。


6
谢谢。我觉得很蠢,因为没有库函数(如果每个人都自己编写代码,那么他们都会以各种不同的方式处理Unicode等问题),但我想这就是现实吧...... - Orion Edwards
C语言的字符串处理很笨拙,特别是在Unicode方面更是如此。C++通过std::string进行了修补,但要使字符串自然需要重新设计。尽管C具有许多优点,但它远非完美的语言。 - David Thornley
2
嘿。C语言是在1972年发明的。Unicode直到90年代才出现。在大部分C语言的历史中,只有ASCII字符集,而str[x]='\0';就可以很好地完成工作。 - T.E.D.
1
这与C语言无关,而是与标准库的不称职有关。 - James M. Lay

19
这激发了我的写作欲望——我不喜欢提供的那些东西。在我看来,应该有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)); 
}

1
我尝试了这个帖子中提到的其他实现方式...一开始它运行得很好,直到突然出现了段错误...我花了很多时间进行调试,发现这是由于修剪代码间接引起的...我尝试了你的版本,段错误消失了 :) 谢谢!(虽然我仍然不明白为什么会出现段错误) - Sadhir
2
编辑:审核我的代码的人建议我检查空输入值..也许这是你想要添加的内容。 - Sadhir
2
“ltrim”返回的指针不适合传递给“free”。如果您使用它(或者当然,使用“trim”),请确保将原始指针保存好 - 尤其是不要尝试像“s = trim(s);”这样的操作,除非您先将“s”存储在其他地方。 - cHao
3
rtrim 为什么不会超过字符串的起始位置?这看起来对我来说不安全... - Joe Teague

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等着您,使您的生活变得更加轻松。

8

看到这样的实现方式,我感到很惊讶。通常我会这样进行修剪:

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

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


3
哦,我认为这可能会导致缓冲区下溢,请考虑以下代码:char buffer[] = " "; trim(buffer); 然后你至少读取buffer[-1],如果它是随机的空格,你甚至会写在你的缓冲区外面。 - quinmars
不错!我已经添加了额外的检查。我也会检查我的生产代码 :) - qrdl

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

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

3
#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

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

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

0
这是我的实现,与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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接