【问题标题】:Cannot create array of substrings split by separator无法创建按分隔符拆分的子字符串数组
【发布时间】:2022-01-04 04:18:05
【问题描述】:

我正在尝试将由空格分隔的多个单词的字符串拆分为子字符串数组。

存在阻止代码正常工作的问题。它成功地找到了断点的索引,但它没有正确打印分隔元素的数组。

代码应该创建一个包含字符串中给定名称的数组。

为什么代码没有按预期运行?

#include <stdio.h>
#include <string.h>

int main() {
    char string [] = " Joe James John Maria Micheal";
    int breakPoints[5];
    char ch = ' ';
    int counter = 0;

    for (int i = 0; i < strlen(string); i++) {
        if (string[i] == ch) {
            breakPoints[counter] = i;
            counter++;
        }
    }

    for (int i = 0; i < 5; i++) {
        printf("%d ", breakPoints[i]);
    }

    char *array[5];
    for (int i = 0; i < 4; i++) {
        strncpy(array[i], string + breakPoints[i], breakPoints[i + 1] - breakPoints[i]);
    }

    for (int i = 0; i < 4; i++) {
        printf("%s ", array[i]);
    }
    
    return 0;
}

【问题讨论】:

  • 在将字符串复制到它们之前,您需要为array[5] char 指针分配内存。
  • 或者,您可以通过使用字符'\0' 覆盖每个“断点”来修改字符串。然后您可以将指针存储到原始字符串中,而不是复制单词。这基本上就是strtok 的工作原理。

标签: arrays c string substring


【解决方案1】:

为了设置array[i] 的值,你必须为其分配内存。除此之外,每个标记的count 字符中存在偏移错误,因此它最后包含了一个空格。这是您的源代码,经过此类编辑(以及一些小的样式更改)。

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

#define SEP ' '

int main(void)
{
    char* string = " Joe James John Maria Micheal";
    int lenght = strlen(string);

    int counter = 0;
    int breakpoints[5];
    for(int i = 0; i < lenght; i++)
        if (string[i] == SEP)
            breakpoints[counter++] = i;

    printf("\nBreakpoints: ");
    for(int i = 0; i < 5; i++)
        printf("%d ", breakpoints[i]);

    char* array[5];
    for(int i = 0; i < 4; i++)
    {
        int start = breakpoints[i] + 1;
        int count = breakpoints[i + 1] - breakpoints[i] - 1;

        array[i] = malloc(sizeof(char) * count + 1);
        strncpy(array[i], &string[start], count);
        array[i][count] = '\0';
    }

    printf("\nTokens: ");
    for(int i = 0; i < 4; i++)
    {
        printf("'%s' ", array[i]);
        free(array[i]);
        array[i] = NULL;
    }

    return 0;
}

【讨论】:

  • 及时,如果您打算将其用作另一段代码的一部分,请注意它会为array 的每个元素动态分配内存,请进行更改以正确处理它。
  • 我强烈建议在答案中释放内存分配,因为它可以帮助读者避免意外导致内存泄漏
  • 已接受建议,但我已将其更改为相同的循环。
  • 另外,修复了每个数组元素上缺少的空字符终止符和可能的悬空指针,因为我们现在正在释放分配的内存!
【解决方案2】:

为什么代码没有按预期运行?

这个

char *array[5];

是5 字符指针的数组。在for 循环中,您将字符串复制到未分配的指针,这是未定义的行为。

即使您为array 指针分配内存,由于以下原因,您的程序输出也不会如您所愿:

原因没有。 1:
这里

for (int i = 0; i < 4; i++) {

您只复制了 4 子字符串,但输入字符串具有由空格 (' ') 字符分隔的 5 子字符串。输出将丢失最后一个子字符串。

原因没有。 2:
在strncpy 中复制时,起始偏移量包括空格前的子字符串,因为breakPoints 数组保留了有关给定输入字符串中空格位置的信息。

按照您编写代码的方式,输入字符串必须有一个空格(分隔符)作为第一个字符,因为在复制子字符串时,您正在将字符从当前处理 breakpoints 数组元素复制到下一个 @ 987654333@ 元素。如果字符串开头没有空格(分隔符),breakpoints 数组的第一个元素将是给定字符串第一个单词之后的第一个空格的位置。

您不需要另一个数组来保存给定输入字符串中有关空格(分隔符)的信息。您可以使用strpbrk() 找出下一个分隔符并将子字符串从当前位置复制到下一个分隔符位置,前提是当前位置指向给定字符串中单词的开头。

你可以这样做:

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

#define MAX_WORDS 100

char * alloc_str (const char * start, const char * end) {
    if (!start || !end || (start >= end)) {
        return NULL;
    }

    char * tmp = malloc (end - start + 1);
    if (tmp) {
        strncpy (tmp, start, end - start);
        tmp[end - start] = '\0';
    } else {
        fprintf (stderr, "Failed to allocate memory\n");
        exit (EXIT_FAILURE);
    }

    return tmp;
}

void dealloc_str (char ** p_substr) {
    if (p_substr) {
        for (int i = 0; p_substr[i]; ++i) {
            free (p_substr[i]);
            p_substr[i] = NULL;
        }
    }
}

int main (void) {
    char string[] = " Joe James John Maria Micheal";
    char * p_substr[MAX_WORDS + 1] = {0};
    char * p_curr = string;
    const char * sep = " ";
    unsigned int word_cnt = 0;

    while (p_curr && *p_curr) {
        char * p_next;

        // skip separator
        p_curr += strspn(p_curr, sep);

        p_next = p_curr;

        // find separator
        p_next = strpbrk (p_next, sep);

        // strpbrk() returns null pointer if no such character
        // exists in the input string which is part of string pointed by sep
        if (!p_next) {
            p_next = p_curr + strlen (p_curr);
        }

        p_substr[word_cnt] = alloc_str (p_curr, p_next);

        if (p_substr[word_cnt]) {
            word_cnt++;
        }

        p_curr = p_next;
    }

    p_substr[word_cnt] = NULL;
    printf ("Number of words in input string : %u\n", word_cnt);

    for (int i = 0; p_substr[i]; ++i) {
        printf ("%d. %s\n", i + 1, p_substr[i]);
    }
 
    dealloc_str (p_substr);

    return 0;
}

输出:

# ./a.out
Number of words in input string : 5
1. Joe
2. James
3. John
4. Maria
5. Micheal

如果您想根据多个分隔符拆分字符串,例如- " !,",那么在上面的代码中,只需将" !,"分配给sep即可。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 2020-10-03
    • 1970-01-01
    • 1970-01-01
    • 2017-06-27
    • 1970-01-01
    • 2011-06-09
    相关资源
    最近更新 更多