在C语言中分割和连接字符串

3
我在大学学过C语言,但已经好多年没有使用了。最近我开始使用C作为编程语言开发一个工具。现在我卡在了一些非常基础的函数上。其中包括如何使用分隔符拆分和连接字符串?(我非常想念Python,甚至是Java或C#!)
下面是我创建的用于拆分字符串的函数,但它似乎不能正常工作。而且,即使这个函数可以工作,分隔符也只能是单个字符。我如何使用字符串作为分隔符?
请有人提供一些帮助吗?
理想情况下,我希望有两个函数:
//  Split a string into a string array
char** fSplitStr(char *str, const char *delimiter);

//  Join the elements of a string array to a single string
char* fJoinStr(char **str, const char *delimiter);

谢谢您,Allen。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

char** fSplitStr(char *str, const char *delimiters)
{
    char * token; 
    char **tokenArray;
    int count=0;
    token = (char *)strtok(str, delimiters); // Get the first token
    tokenArray = (char**)malloc(1 * sizeof(char*));

    if (!token) {       
        return tokenArray;  
} 

    while (token != NULL ) { // While valid tokens are returned     
        tokenArray[count] = (char*)malloc(sizeof(token));
        tokenArray[count] = token;
        printf ("%s", tokenArray[count]);    
        count++;
        tokenArray = (char **)realloc(tokenArray, sizeof(char *) * count);      
        token = (char *)strtok(NULL, delimiters); // Get the next token     
} 
    return tokenArray;
}

int main (void)
{
    char str[] = "Split_The_String";
    char ** splitArray = fSplitStr(str,"_");
    printf ("%s", splitArray[0]);
    printf ("%s", splitArray[1]);
    printf ("%s", splitArray[2]);
    return 0;
}

答案:(感谢 Moshbear、Joachim 和 sarnold):

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

char** fStrSplit(char *str, const char *delimiters)
{
    char * token; 
    char **tokenArray;
    int count=0;
    token = (char *)strtok(str, delimiters); // Get the first token
    tokenArray = (char**)malloc(1 * sizeof(char*));
    tokenArray[0] = NULL;
    if (!token) {       
        return tokenArray;  
    } 
    while (token != NULL) { // While valid tokens are returned
        tokenArray[count] = (char*)strdup(token);
        //printf ("%s", tokenArray[count]);
        count++;
        tokenArray = (char **)realloc(tokenArray, sizeof(char *) * (count + 1));
        token = (char *)strtok(NULL, delimiters); // Get the next token
    }
    tokenArray[count] = NULL;  /* Terminate the array */
    return tokenArray;
}

char* fStrJoin(char **str, const char *delimiters)
{
    char *joinedStr;
    int i = 1;
    joinedStr = realloc(NULL, strlen(str[0])+1);
    strcpy(joinedStr, str[0]);
    if (str[0] == NULL){
        return joinedStr;
    }
    while (str[i] !=NULL){
        joinedStr = (char*)realloc(joinedStr, strlen(joinedStr) + strlen(str[i]) + strlen(delimiters) + 1);
        strcat(joinedStr, delimiters);
        strcat(joinedStr, str[i]);
        i++;
    }
    return joinedStr;
}


int main (void)
{
    char str[] = "Split_The_String";
    char ** splitArray = (char **)fStrSplit(str,"_");
    char * joinedStr;
    int i=0;
    while (splitArray[i]!=NULL) {
        printf ("%s", splitArray[i]);
        i++;
    }
    joinedStr = fStrJoin(splitArray, "-");
    printf ("%s", joinedStr);
    return 0;
}
3个回答

7
请使用strpbrk代替strtok,因为strtok有两个弱点:
  • 它不是可重入的(即线程安全)
  • 它修改了字符串
对于连接,请使用strncat进行连接,并使用realloc进行调整大小。操作的顺序非常重要。
在执行realloc;strncat循环之前,请将目标字符串的第0个元素设置为'\0',以便strncat不会导致未定义的行为。

谢谢Moshbear。我将尝试使用strpbrk函数。然而,代码中当前的问题是它只能打印第一个元素。当执行“printf("%s",splitArray [1]);”行时遇到错误。有什么想法吗?我感觉这一行有些问题 - “tokenArray =(char **)realloc(tokenArray,sizeof(char count);”,但不知道具体原因。 - Allen Qin
你需要重新分配 sizeof(char *) * (count + 1) 的内存空间。此外,在输入到 strpbrk 函数时,需要在计算出标记长度后递增输入字符串指针,以避免无限循环。这是因为 strpbrk 的行为类似于 C++ 的 std::find_first_of,所以必须递增指针直到指向不在分隔符集合中的第一个字符。 - moshbear
谢谢Moshbear。将那一行改为“sizeof(char *) * (count + 1)”后,它可以正常工作了。 - Allen Qin

3

首先,不要使用sizeof来获取字符串的长度。应该使用strlen函数。在这种情况下,strdup更好。

并且你实际上不是复制strtok返回的字符串,而是复制指针。将你的循环更改为以下内容:

while (token != NULL) { // While valid tokens are returned
    tokenArray[count] = strdup(token);
    printf ("%s", tokenArray[count]);
    count++;
    tokenArray = (char **)realloc(tokenArray, sizeof(char *) * count);
    token = (char *)strtok(NULL, delimiters); // Get the next token
}
tokenArray[count] = NULL;  /* Terminate the array */

此外,当你完成使用数组时,请不要忘记释放数组中的条目和数组本身。 编辑fSplitStr 的开头处,等到检查 token 不是 NULL 后再分配 tokenArray,如果 tokenNULL,为什么不返回 NULL 呢?

非常感谢Joachim。我尝试了你的代码,但是得到了相同的错误。在执行“printf(%s”,splitArray [1]);”行时遇到了未处理的异常。但是,第一个元素可以正确打印。此外,“printf(%s”,tokenArray [count]);”语句可以在循环内打印所有元素。 - Allen Qin
@Allen,你的代码对我来说是可行的,就像你在问题中提供的那样。没有任何异常或崩溃。 - Some programmer dude
感谢您的帮助。在Moshbear建议的更改后,它现在可以正常工作了。此外,如果第一个标记为NULL,我将进行更改以返回“NULL”。 - Allen Qin

1

我不确定对你来说什么是最好的解决方案,但我有一些笔记:

token = (char *)strtok(str, delimiters); // Get the first token
tokenArray = (char**)malloc(1 * sizeof(char*));

if (!token) {       
    return tokenArray;  
}

如果在字符串中未能找到任何标记,请返回指向足以容纳单个字符指针的“数组”的指针。它未初始化,因此不建议以任何方式使用此数组的内容。 C语言几乎从不为您初始化内存为0x00。(calloc(3)会为您执行此操作,但由于您需要覆盖每个元素,因此似乎没有切换到calloc(3)值得。)

此外,在malloc(3)调用之前的(char **)情况表明您可能已经忘记了#include <stdlib.h>,这将适当地为malloc(3)原型化。 (在1989年之前,强制转换是必需的。)

请注意,你的 while() { } 循环正在将指针设置为原始输入字符串中的部分,以便用于你的 tokenArray 元素。(这是 moshbear 在他的答案中提到的一种缺点之一 -- 虽然它并不总是一种弱点。) 如果你改变了 tokenArray[1][1]='H',那么原始输入字符串也会发生变化。(除了将每个分隔符替换为 ASCII NUL 字符外。)

感谢您的评论,我会按照您的建议进行更改。我添加(char **)的原因是代码在该工具(HP Loadrunner)中无法正常工作。我将调查是否添加#include <stdlib.h>可以解决问题。 - Allen Qin

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