【问题标题】:Combining Strings into a List in C在 C 中将字符串组合成一个列表
【发布时间】:2017-02-10 08:36:18
【问题描述】:

以下是我针对以下问题的代码。我正在尝试获取以逗号分隔的一串名字和一串姓氏,并将它们转换为全名列表。例如,如果firstnames = "John,Jane"lastnames = "Smith,Doe",那么输出应该是["John Smith", "Jane Doe"]

我相信我的问题是在我使用 strtok 时出现的,因为 first_names[i] = name 给了我一个错误。对此的任何帮助将不胜感激!

char **combine_names(char *firstnames, char *lastnames) {
    char first_names[50][50];
    char last_names[50][50];
    int i = 0;
    char *name = strtok(firstnames, ",");
    while (name != NULL) {
        first_names[i] = name;
        i++;
        name = strtok(NULL, ",");
    }
    i = 0;
    name = strtok(lastnames, ",");
    while (name != NULL) {
        last_names[i] = name;
        i++;
        name = strtok(NULL, ",");
    }
    char **names;
    names = malloc(strlen(first_names) * sizeof(char*));
    for (int i = 0; i < strlen(first_names); i++) {
        names[i] = malloc(51 * sizeof(char));
    }
    int i = 0;
    int j = 0;
    int k = 0;
    while (first_names[i] != '\0') {
        while (first_names[i][j] != '\0') {
            names[i][j] = first_names[i][j];
            j++;
        }
        names[i][j] = ' ';
        j++;
        while (second_names[i][k] != '\0') {
            names[i][j] = second_names[i][k];
            j++;
            k++;
        }
        names[i][j] = '\0';
        i++;
    }
    names[i] = '\0';
    return names;
}

以下行导致与第一个参数不兼容的指针错误。这是为什么呢?

names = malloc(strlen(first_names) * sizeof(char*));

【问题讨论】:

  • 例如first_names[i] 是一个数组。你不能分配给一个数组,但你可以copy 给它。
  • And first_names 不是字符串,它不是你可以调用 strlen 并期望得到有效结果的东西。尝试真正阅读编译器给你的错误和警告。

标签: c arrays string split malloc


【解决方案1】:

使用strtok() 确实会带来一些问题,但主要问题是您使用无效表达式malloc(strlen(first_names) * sizeof(char*)); 分配namesfirst_names 不是 C 字符串,strlen(first_names) 不计算 first_names 数组中的条目数。

这是一种更简单、更安全的方法:

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

char **combine_names(const char *firstnames, const char *lastnames) {
    int n = 0;
    char **names = malloc(sizeof(*names) * (n + 1));
    char *p;

    if (names == NULL) {
        perror("cannot allocate memory\n");
    }

    while (*firstnames && *lastnames) {
        int len1 = strcspn(firstnames, ",");
        int len2 = strcspn(lastnames, ",");
        int size = len1 + 1 + len2 + 1;
        p = malloc(size);
        if (p == NULL) {
            perror("cannot allocate memory\n");
        }
        snprintf(p, size, "%.*s%s%.*s",
                 len1, firstnames,
                 len1 && len2 ? " " : "",
                 len2, lastnames);
        names = realloc(names, sizeof(*names) * (n + 2));
        if (names == NULL) {
            perror("cannot allocate memory\n");
        }
        names[n++] = p;
        firstnames += len1 + (firstnames[len1] == ',');
        lastnames += len2 + (lastnames[len2] == ',');
    }
    names[n] = NULL;
    return names;
}

【讨论】:

  • n 是组合数组中的字符串数。该数组分配给NULL 终止符的n + 1 条目,否则将无法告诉调用者已组合了多少项。此约定在 main() 中用于 argv[] 数组。
  • 谢谢!我想我让它变得比需要的更难。
  • 您的多阶段方法是硬编码隐含的最大组合数和名称长度。考虑并行增量解决方案需要一些练习。有时它们很难实施,有时是不可能的。在这种情况下,为每个新组合重新分配数组的效率会有些低,但简单是一个基本价值,除非您的输入字符串非常长,否则这种增量解决方案就足够了。
【解决方案2】:

请记住,first_names 是一个双精度字符数组。这意味着first_names[i] 实际上是一个字符串,或者一个字符数组。您不能直接分配给字符数组,而是必须逐个字符地写入数组。最简单的方法是使用字符串复制或 strcpy(first_names[i], name),但 strcpy 不能防止缓冲区溢出。一种方法是使用strncpy,只是要小心,因为当源字符串超过目标字符串的大小时,这并不能保证字符串以空值结尾。要解决此问题,请使用

strncpy(first_names[i], name, 50);
first_names[i][49] = '\0';

考虑到strncpy 的缺点,最好使用类似于@chqrlie 的解决方案。

【讨论】:

  • 如果源太长,strncpy 将不会终止目标,从而导致问题。此功能容易出错,请勿使用。见randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already
  • 发布的链接没有解决strncpy 的问题。 memset 是多余的,first_names[i][49] = '\0'; 就足够了。新手程序员应该被大声警告这个容易出错的功能。 不要随便使用。阅读:blog.liw.fi/posts/strncpy
  • @chqrlie,正如你所说,正在解决这个问题,对不起,我对此有点陌生,只是提出一种解决方法,考虑到它的缺陷,我不鼓励反对 strncpy。跨度>
  • first_names[49] = '\0' -> first_names[i][49] = '\0';为了安全起见,您可以使用 strncpy 并不是气馁的声明。
  • @chqrlie 已编辑,感谢您抽出宝贵时间审阅。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-30
相关资源
最近更新 更多