Tuesday, May 11, 2010

split()函数的C、C++标准库实现

2010.5.11 22:52更新: 在大猫同学的强烈要求下, 增加了测试代码中动态分配空间的销毁.

2010.5.14 22:46再更新: 上次更新代码之后, 大猫同学指出还是存在内存泄漏问题, 各位同学一定要注意malloc()free()必须成对使用哦~

最近有这么一个需求, split()函数大家都用过, 将字符串按特定的分隔符 (delimiter) 分割成几个部分, 这在脚本语言中早已司空见惯. 但C/C++中是没有的, 于是就给了我这样闲得蛋疼的人消磨时间的机会, 下面分别是用C和C++的标准库实现的split()函数.

首先是C语言的实现, 这个是我自己写的, 函数返回一个指向char*数组 (也就是字符串数组) 的指针.
#include <string.h>
#include <stdlib.h>

/*
 * split - Break a string @s into an array of strings.
 * @s: The string to be split.
 * @delim: The delimiter.
 * @n: The length of the array of strings, created by splitting @s.
 */
char** split(char* s, char delim, int* n)
{
    char* p;
    char* q;
    *n = 1;
    p = q = s;
    /*
     * Calculate the value of @n.
     */
    while ((p = strchr(q, delim)) != NULL)
    {
        ++*n;
        q = p + 1;  // skip the delimiter
    }
    p = q = s;

    char** sep = (char**) malloc(sizeof(char*) * (*n));  // the array of strings
    int i;
    for (i = 0; i != *n; ++i, q = p + 1)
    {
        /*
         * When there isn't delimiter, put `p' point to the end of @s.
         * Same like behavior of strchrnul() library function.
         */
        if ((p = strchr(q, delim)) == NULL)
            p = s + strlen(s);

        int len = p - q;
        sep[i] = (char*) malloc(sizeof(char) * (len + 1));
        strncpy(sep[i], q, len);
        sep[i][len] = '\0';
    }

    return sep;
}
可以使用下面的代码进行测试:
#include <stdio.h>
#include <stdlib.h>

char** split(char*, char, int*);

int main(int argc, char* argv[])
{
    char str[] = ":GFW::is:evil:";
    int i, n;
    char** sep = split(str, ':', &n);
    for (i = 0; i != n; ++i)
        printf("%s\n", sep[i]);

    for (i = 0; i != n; ++i)
    {
        free(sep[i]);
        sep[i] = NULL;
    }
    free(sep);
    sep = NULL;

    return 0;
}

C++实现来自Stack Overflow的一篇讨论, 我稍微进行了一些修改.
#include <sstream>
#include <vector>

using namespace std;

vector<string> split(const string s, char delim = ' ')
{
    vector<string> elems;
    istringstream ss(s);
    string item;
    while (getline(ss, item, delim))
    {
        elems.push_back(item);
    }
    if (item == "")
        elems.push_back(item);
    return elems;
}
代码很简洁, 测试代码就不写了, 大家应该都能看懂. 其实大名鼎鼎的Boost也实现了split(), 但毕竟不是任何时候都能使用Boost.

No comments:

Post a Comment