在C语言中不使用任何预定义函数将字符串分割成单词数组

3

我正在尝试创建一个函数,该函数接受一个字符串,将其分割成单词并返回包含这些单词的数组。在“分割”函数中,除了malloc之外,不允许使用任何预制函数。最后,我必须以以下格式设置我的函数:char **ft_split_whitespaces(char *str)


    d this is me
    s is me
    s me
    r

预期输出:


    Hello
    World
    This
    Is
    Me

完整的代码如下:


    #include <stdio.h>
    #include <stdlib.h>
    
    int     count_words(char *str)
    {
        int i; 
        int word;
        
        i = 0;
        word = 1;
        while(str[i]!='\0')
        {
            if(str[i]==' ' || str[i]=='\n' || str[i]=='\t' 
            || str[i]=='\f' || str[i]=='\r' || str[i]=='\v')
                word++;
            i++;
        }
        return (word);
    }
    
    char    **ft_split_whitespaces(char *str)
    {
        int index;
        int size;
        int index2;
        char **arr;
        
        index = 0;
        index2 = 0;
        size = count_words(str);
        arr = (char **)malloc(size * sizeof(char));
        if (arr == NULL)
            return ((char **)NULL);
        while (str[index])
        {
            if(str[index] == ' ')
            {
                index++;
                value++;
                index2++;
            }
            else
                *(arr+index2) = (char*) malloc(index * sizeof(char));
                *(arr+index2) = &str[index];    
            index++;
        }
        **arr = '\0';
        return (arr);
    }
    
    int main()
    {
        char a[] = "Hello World This Is Me";
        char **arr;
        int i;
        int ctr = count_words(a);
        arr = ft_split_whitespaces(a);
        
        for(i=0;i < ctr;i++)
            printf("%s\n",arr[i]);
        return 0;
    }


这是一个很好的问题。它在这些课程笔记中有讨论。还可以参考这个问题 - Steve Summit
调用 ft_split_whitespaces 的代码应该如何知道返回的数组有多少项?末尾是否应该有一个 NULL 值?(如果是这样,请不要忘记为该指针分配空间!) - David Schwartz
ft_split_whitespaces()可以修改原始字符串(例如,在单词末尾插入零终止符并返回指向单词开头的指针数组,以避免为每个单词分配内存)吗?您还可以使用递归来避免count_words() - Brendan
如果您正在使用C编译器编译代码,那么不应该强制转换malloc的返回值。在C中,您不需要对malloc的返回值进行强制转换。但是,在某些情况下,例如使用C++编译器时,需要进行强制转换。 - Jerry Jeremiah
1个回答

3
你的程序有很多错误: 1. arr = (char **)malloc(size * sizeof(char)); 不正确,因为 arrchar** 类型,应该使用 sizeof(char*) 或更好的 (sizeof(*arr)),因为现代系统中 sizeof(char) 通常不等于 sizeof(char*)。 2. 在 ft_split_whitespaces 中,在 else 语句周围没有花括号 {},可能是你想要的。这样你的条件逻辑会出问题。 3. 在 while 循环中,你为每个非空格字符分配一个新的 char[]。你应该只为每个新单词分配一个,然后在该数组中 只填充 字符。 4. *(arr + index2) = &str[index]; 这并不像你想象的那样工作,它仅仅将字符串指向 *(arr + index2) 和偏移量为 indexstr。你需要逐个复制每个字符或使用 memcpy()(在问题中可能无法使用)。这就解释了为仅提供主字符串中的偏移量而不是实际标记的原因。 5. **arr = '\0'; 你会丢失在 arr 的第0个索引中存储的任何内容。你需要逐个附加 \0arr 中的每个字符串。 6. *(arr + index2) = (char*) malloc(index * sizeof(char)); 你最终将分配递增大小的 char 数组,因为你正在使用字符数的 index,该字符数不断增加。你需要确定字符串中每个标记的正确长度,并适当地分配存储空间。
此外,为什么要用 *(arr + index2)? 为什么不使用更易读的 arr[index2]
进一步的澄清: 考虑 str = "abc de" 你将从以下内容开始:
*(arr + 0) = (char*) malloc(0 * sizeof(char));
//ptr from malloc(0) shouldn't be dereferenced and is mostly pointless (no pun), probably NULL
*(arr + 0) = &str[0]; 

这里的str[0] = 'a'是内存中的某个位置,因此在执行&str[0]时,你将把该地址存储在*(arr + 0) 中。

现在在下一次迭代中,你将有

*(arr + 0) = (char*) malloc(1 * sizeof(char)); 
*(arr + 0) = &str[1]; 

这次你再次用不同的地址替换了之前在相同index2处malloc的数组。在下一次迭代中,*(arr + 0) = (char*) malloc(2 * sizeof(char));。您最终将重置相同的*(arr + index2)位置,直到遇到空格,然后对下一个单词重复相同的操作。因此,不要为每个index值分配数组,而只有在需要时才分配。此外,这表明您将随着index的增加而不断增加传递给malloc的大小,这就是#6所示的内容。
谈到&str[index]
您正在设置(arr + index2),即指向char的指针char*,指向另一个char*。在C中,将指针设置为另一个指针并不会将第二个指针的内容复制到第一个指针中,而只是使它们都指向同一个内存位置。因此,当您设置类似*(arr + 1) = &str[4]的东西时,它只是原始字符串index = 4的指针。如果尝试打印此*(arr + 1),您将只得到从index=4到字符串结尾的子字符串,而不是您要获取的单词。 **arr = '\0'只是解引用指向*arr的指针并将其值设置为\0。因此,假设您有*(arr + 0) ="hello\0",则将其设置为"\0ello\0"。如果您在迭代此字符串,则永远不会遍历超出第一个'\0'字符。因此,您会丢失任何先前指向的*arr
此外,*(arr + i)arr[i]完全等效,并且可读性更好。它更好地传达了arr是一个数组,arr[i]是对第 i 个元素进行分析。

非常感谢您的评论,但我必须承认,从第3步开始,我真的不明白该如何解决这些问题。我必须说,我对指针和malloc的实际操作还很陌生,如果可能的话,请进一步澄清一下,那就太好了!无论如何,还是非常感谢! - Alsakka
1
@Alsakka 我已经添加了进一步的解释。修复这些问题,你应该就有一个可行的解决方案了。我不会直接给出确切的答案,因为这不是 SO 的目的,但我希望你能够看到并自己解决问题。你正在正确的轨道上。祝你好运! - Zoso

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