从C中的另一个数组创建数组

时间:2017-01-02 03:30:16

标签: c arrays string algorithm loops

我试图从另一个数组创建一个数组,如果我有char *arr[100] = {"Hi", "&&", "hello", 0};我想让它成为new[0] = "hi"; new[1] = "hello";我的代码似乎不起作用。我该如何解决这个问题?

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

void    split_by_word(char *av[], char **arr, char *word)
{
    int i = 0;
    int j = 0;

    while (strcmp(*arr, word) == 0)
        arr++;
    if (!arr)
        return ;
    while (arr[i])
    {
        strcat(av[j], arr[i]);
        if (strcmp(*arr, word) == 0)
            j++;
        i++;
    }
}

int main()
{
    char *av[100];
    char *arr[100] = {"hi", "&&", "hello", 0};
    memset(av, 0, sizeof(char *) * 100);
    split_by_word(av, arr, "&&");
    return 0;
}

给定数组

char *arr[] = 
{
 "Hello", "good", 
 "morning", "out",
 "hello", "good", 
 "afternoon", 0
};

我分出时的输出(split_by_word(av, arr, "out"));

 av[0] = "hello good morning";
 av[1] = "hello good afternoon";

4 个答案:

答案 0 :(得分:1)

您需要为新的2D数组分配空间以便开始。为简单起见,我分配了一个大小为100 x 10的文件。 *

此外,逻辑更简单,我会说,循环遍历你的数组,如果 word,那么复制它,否则什么都不做(跳过它,如果换句话说就是word

因此,一个基本的好例子是:

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

void split_by_word(char av[100][10], char **arr, char *word)
{
    int i = 0, j = 0;
    while(arr[i])
    {
        // if not 'word', copy
        if(strcmp(arr[i], word))
            strcpy(av[j++], arr[i]);
        ++i;
    }
}

int main()
{
    int i;
    char av[100][10] = {{0}};
    char *arr[100] = {"hi", "&&", "hello", 0};
    split_by_word(av, arr, "&&");
    for(i = 0; i < 2; ++i)
        printf("%s\n", av[i]);
    return 0;
}

输出:

Georgioss-MacBook-Pro:~ gsamaras$ gcc -Wall main.c 
Georgioss-MacBook-Pro:~ gsamaras$ ./a.out 
hi
hello

*对于2D动态分配的数组,我会这样做2d-dynamic-array-c

答案 1 :(得分:1)

这里有一些代码似乎可以根据您修改过的问题的要求运行。我毫不怀疑可以通过一些努力来改进 - 特别是在split_by_word()。您修改后的要求似乎会连接字符串,而您原来的要求确实不明确。

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

static void split_by_word(char **av, char **arr, char *word)
{
    while (*arr != 0)
    {
        if (strcmp(*arr, word) == 0)
            av++;
        else if (*av == 0)
            *av = strdup(*arr);
        else
        {
            size_t len = strlen(*av) + strlen(*arr) + 2;  // 1 for null byte, 1 for blank
            void *space = realloc(*av, len);
            if (space == 0)
            {
                fprintf(stderr, "Memory allocation failed (%zu bytes)\n", len);
                exit(EXIT_FAILURE);
            }
            *av = space;
            strcat(*av, " ");
            strcat(*av, *arr);
        }
        arr++;
    }
    *++av = 0;  // Null terminate pointer list
}

static void free_words(char **words)
{
    while (*words != 0)
    {
        free(*words);
        *words++ = 0;
    }
}

static void print_words(char **words)
{
    for (int i = 0; words[i] != 0; i++)
        printf("%d: [%s]\n", i, words[i]);
}

int main(void)
{
    char *av[100] = { 0 };
    char *arr1[100] = { "hi", "&&", "hello", 0 };
    split_by_word(av, arr1, "&&");
    print_words(av);
    free_words(av);

    char *arr2[] =
    {
        "Hello", "good",
        "morning", "out",
        "hello", "good",
        "afternoon", 0
    };

    split_by_word(av, arr2, "out");
    print_words(av);
    free_words(av);

    return 0;
}

示例输出:

0: [hi]
1: [hello]
0: [Hello good morning]
1: [hello good afternoon]

答案 2 :(得分:1)

您需要确保您了解您的arr指向字符串文字的指针数组,其中有标记,用于指示内容的分隔位置将数组放入由文字组成的单独字符串中,arr最终由 sentinel nul终止。

一个问题是如何处理由arr中的单词创建的字符串长度的变化。根据{{​​1}}中单词的长度,您如何确保组成结果数组的组合字符串有足够的空间?

您可以猜测并为结果数组中的每个元素设置一个静态存储大小(希望对于您需要分离的任何arr足够大),或者您可以动态分配(根据需要分配/重新分配)。这样您就可以确保在结果数组中处理arr的内容。

有很多方法可以执行此操作,您可以使用许多例程。无论如何,方法基本相同。读取arr中的每个单词,确保结果字符串有足够的存储空间,然后将arr中的单词连接到结果字符串。一种方法如下:

arr

高于#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXS 16 int split_by_word (char **res, char **arr, char *tok); void *xrealloc (void *ptr, size_t psz, size_t *nelem, size_t inc); int main (void) { char *arr[] = { "Hello", "good", "morning", "out", "hello", "good", "afternoon", 0 }, *res[sizeof arr/sizeof *arr] = { NULL }, *tok = "out"; if (split_by_word (res, arr, tok) > 0) for (int i = 0; res[i]; i++) { printf ("%s\n", res[i]); free (res[i]); } return 0; } int split_by_word (char **res, char **arr, char *tok) { int aidx = 0, cidx = 0, ridx = 0; /* array, current and result index */ size_t szres = MAXS; /* current size of res[ridx] */ if (!res || !arr || !tok) return -1; /* validate parameters */ if (!(res[ridx] = calloc (szres, sizeof *(res[ridx])))) /* allocate result */ return -1; while (arr[aidx]) { if (strcmp (arr[aidx], tok) == 0) { /* separator found */ *(res[ridx] + cidx) = 0; /* nul-terminate */ ridx++; /* advance result index */ szres = MAXS; /* reset alloc size, alloc */ if (!(res[ridx] = calloc (szres, sizeof *(res[ridx])))) return -1; cidx = 0; /* reset current index */ } else { /* append word from arr to res */ size_t len = strlen (arr[aidx]), /* get length */ reqd = cidx ? len + 2 : len + 1; /* add space and nulbyte */ if (cidx + reqd > szres) /* check space, realloc */ res[ridx] = xrealloc (res[ridx], sizeof *(res[ridx]), &szres, cidx + reqd); /* write word to result */ snprintf (res[ridx] + cidx, reqd, cidx ? " %s" : "%s", arr[aidx]); cidx += reqd - 1; /* advance current index */ } aidx++; /* advance array index */ } *(res[ridx] + cidx) = 0; /* nul-terminate */ return ridx ? ridx : cidx ? 1 : ridx; /* return strings in results */ } /** realloc 'ptr' to 'nelem' of 'psz' to 'nelem + inc' of 'psz'. * returns pointer to reallocated block of memory with all new * memory initialized to 0/NULL. return must be assigned to * original pointer in caller. */ void *xrealloc (void *ptr, size_t psz, size_t *nelem, size_t inc) { void *memptr = realloc ((char *)ptr, (*nelem + inc) * psz); if (!memptr) { fprintf (stderr, "realloc() error: virtual memory exhausted.\n"); exit (EXIT_FAILURE); } /* zero new memory (optional) */ memset ((char *)memptr + *nelem * psz, 0, inc * psz); *nelem += inc; return memptr; } 会返回一个整数值,表示结果数组中的字符串数,或者出错时为split_by_word

示例使用/输出

-1

验证内存使用

如果你分配内存,你有责任保留一个指向每个块的begninning的指针,这样就可以在不再需要时释放它。在Linux上,$ ./bin/splitap Hello good morning hello good afternoon 是首选工具。只需通过它运行您的程序。 (每个操作系统都有类似的内存错误检查例程)

valgrind

您想要验证每个分配是否已被释放,是否可能发生内存泄漏,并且您使用已分配的内存的方式没有错误(例如无效的读/写等)。

答案 3 :(得分:0)

由于它(从您的声明中)看起来像您只想在新数组中存储指针,因此不需要strcat()strcpy()。函数中的第一个循环似乎是跳过初始分隔符,但您可以在主循环中执行此操作。以下是代码的修改版本:

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

void split_by_word(char *av[], char **arr, char *word)
{
    size_t i = 0;
    size_t j = 0;

    while (arr[i]) {
        if (strcmp(arr[i], word)) {
            av[j] = arr[i];
            ++j;
        }
        ++i;
    }
}

int main(void)
{
    char *av[100];
    char *arr[100] = {"hi", "&&", "hello", 0};
    memset(av, 0, sizeof(char *) * 100);
    split_by_word(av, arr, "&&");

    for (size_t i = 0; av[i]; i++)
        puts(av[i]);

    return 0;
}

arr传递给split_by_word()后,av包含指向字符串文字"hi""hello"的指针:

λ> ./a.out
hi
hello

另一方面,如果您确实希望新数组包含字符串的副本,则必须声明av以便为这些副本留出空间,并且您需要使用strcpy()或者一些类似的函数,将字符复制到数组中。这是另一个完成此任务的版本。请注意,必须事先知道最大字符串的大小;为此,我#define d为常数。另请注意,显示循环与前一循环略有不同。第一个显示循环一直持续到遇到NULL指针,但在第二个版本中循环继续,直到遇到空字符串。输出与以前相同。

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

#define MAXWORD  100

void split_by_word(char av[][MAXWORD], char **arr, char *word)
{
    size_t i = 0;
    size_t j = 0;

    while (arr[i]) {
        if (strcmp(arr[i], word)) {
            strcpy(av[j], arr[i]);
            ++j;
        }
        ++i;
    }
}

int main(void)
{
    char av[100][MAXWORD] = { { 0 } };
    char *arr[100] = {"hi", "&&", "hello", 0};

    split_by_word(av, arr, "&&");

    for (size_t i = 0; av[i][0]; i++)
        puts(av[i]);

    return 0;
}

更新

我修改了之前的解决方案,以满足修订示例中建议的精确要求。常量MAXWORD现在是MAXLEN,并且足够大以容纳几个字。使用strcat()代替strcpy(),每次添加一个单词时,都会在字符串的末尾添加一个额外的空格字符。只有遇到分隔符字符串时,字符串索引j才会递增。

请注意,没有检查可以确保av中有一个新字符串的空间(当前最多可以容纳99个字符串,一个空字符串作为终结符),或者是新的空间字符串中的单词(999个字符加上'\ 0'终结符的空间似乎相当慷慨)。这里没有动态分配,如果您需要,Jonathan Leffler's solution可能更符合您的口味。

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

#define MAXLEN  1000

void split_by_word(char av[][MAXLEN], char **arr, char *word)
{
    size_t i = 0;
    size_t j = 0;

    while (arr[i]) {
        if (strcmp(arr[i], word)) {
            strcat(av[j], arr[i]);
            strcat(av[j], " ");
        } else {
            ++j;
        }
        ++i;
    }
}

int main(void)
{
    char av[100][MAXLEN] = { { 0 } };
    char *arr[] = 
        {
            "Hello", "good", 
            "morning", "out",
            "hello", "good", 
            "afternoon", 0
        };

    split_by_word(av, arr, "out");

    for (size_t i = 0; av[i][0]; i++)
        puts(av[i]);

    return 0;
}

以下是该计划的输出:

λ> ./a.out 
Hello good morning 
hello good afternoon 

Bounds Checking

我不忍心离开这个而不添加对数组边界的一些检查,以避免在意外输入大小的情况下出现未定义的行为。以下是split_by_word()函数的一个版本,如果有空间,则仅向av添加新字符串,并且只有空格时才向字符串添加新字。如果没有足够的空间用于新单词,则该函数将跳至下一个分隔符或arr的末尾,以先到者为准。我为要存储的最大字符串数添加了MAXNUM常量,以替换以前版本中的硬编码100。我毫不怀疑你可以改进这个功能。

#define MAXNUM  100
#define MAXLEN  1000

void split_by_word(char av[][MAXLEN], char **arr, char *word)
{
    size_t i = 0;
    size_t j = 0;

    while ((j + 1) < MAXNUM && arr[i]) {
        if (strcmp(arr[i], word)) {
            /* Verify space for word + extra space */
            if ((strlen(av[j]) + strlen(arr[i]) + 1) < MAXLEN) {
                strcat(av[j], arr[i]);
                strcat(av[j], " ");
            } else {                // No space: skip to next delimiter
                ++i;
                while (arr[i] && strcmp(arr[i], word)) {
                    ++i;
                }
                ++j;                // increment to next string
            }
        } else {
            ++j;                    // increment to next string
        }
        if (arr[i]) ++i;            // increment i if not already at end
    }
}