c - 数组未正确填充

标签 c arrays

我试图将文档解构为其各自的段落,并将每个段落作为字符串输入到数组中。但是,每次添加新值时,它都会覆盖数组中所有先前的值。最后读取的“段落”(由换行符表示)是数组中每个非空值的值。

这是代码:

char buffer[MAX_SIZE];
char **paragraphs = (char**)malloc(MAX_SIZE * sizeof(char*));
int pp = 0;
int i;

FILE *doc;
doc = fopen(argv[1], "r+");
assert(doc);

while((i = fgets(buffer, sizeof(buffer), doc) != NULL)) {
    if(strncmp(buffer, "\n", sizeof(buffer))) {
        paragraphs[pp++] = (char*)buffer;
    }
}

printf("pp: %d\n", pp);

for(i = 0; i < MAX_SIZE && paragraphs[i] != NULL; i++) {
    printf("paragraphs[%d]: %s", i, paragraphs[i]);
}

我收到的输出是:

pp: 4
paragraphs[0]: paragraph four
paragraphs[1]: paragraph four
paragraphs[2]: paragraph four
paragraphs[3]: paragraph four

当程序运行如下:./prog.out doc.txt,其中doc.txt是:

paragraph one
paragraph two
paragraph three

paragraph four

程序的行为是另外所期望的。段落计数工作正常,忽略仅包含换行符的行(第 4 行)。

我认为问题发生在 while 循环中,但我不确定如何解决该问题。

最佳答案

你的解决方案非常合理。您的段落数组应该保存每个段落,并且由于每个段落元素只是一个小的 4 字节指针,因此您可以定义合理的最大数量。然而,由于这个最大数量是一个常量,动态分配数组的用处不大。
动态分配的唯一有意义的用途是读取整个文本一次以计算实际的段落数,相应地分配数组并再次重新读取整个文件,但我怀疑这是否值得。 使用固定大小的段落数组的缺点是,一旦达到最大元素数,您必须停止填充它。
如果您绝对希望能够处理整本圣经,那么您可以重新分配更大的数组,但对于教育练习,我认为停止记录段落是合理的(从而生成可以存储和计算段落的代码最多可达最大数量)。

代码的真正问题是,您没有将段落内容存储在任何地方。当您读取实际行时,它始终位于同一缓冲区内,因此每个段落都将指向相同的字符串,该字符串将包含读取的最后一个段落。

解决方案是制作缓冲区的唯一副本并使当前段落指向该副本。

C 已经够困惑了,我建议使用 strdup()函数,它dup复制一个string(基本上计算字符串长度,分配足够的内存,将字符串复制到它并返回保存新副本的新内存块)。您只需要记住在使用完这个新副本后释放它(在您的情况下是在程序结束时)。

不是最省时的解决方案,因为每个字符串都需要 strlen和一个 mallocstrdump 内部执行虽然您可以为所有段落预先分配一个大缓冲区,但它肯定更简单,并且可能更节省内存(仅为每个字符串分配最少量的内存,尽管每个 malloc 消耗一些额外的字节内部分配器管理)。

该死的尴尬fgets还存储尾随 \n位于该行的末尾,因此您可能想要删除它。

如果您只是使用 pp 作为限制,而不是检查未初始化的段落,那么您的最后一个显示循环将会更简单、更健壮且更高效。

最后,您最好为最大行号和最大段落数定义两个不同的常量。对两者使用相同的值没有什么意义,除非您正在处理完美的方形文本:)。

#define MAX_LINE_SIZE   82 // max nr of characters in a line (including trailing \n and \0)
#define MAX_PARAGRAPHS 100 // max number of paragraphs in a file

void main (void)
{
    char buffer[MAX_LINE_SIZE];
    char * paragraphs[MAX_PARAGRAPHS];
    int pp = 0;
    int i;

    FILE *doc;
    doc = fopen(argv[1], "r+");
    assert(doc != NULL);

    while((fgets(buffer, sizeof(buffer), doc) != NULL)) {
        if  (pp != MAX_PARAGRAPHS  // make sure we don't overflow our paragraphs array
          && strcmp(buffer, "\n")) {
            // fgets awkwardly collects the ending \n, so get rid of it
            if (buffer[strlen(buffer)-1] == '\n') buffer[strlen(buffer)-1] = '\0';
            // current paragraph references a unique copy of the actual text
            paragraphs[pp++] = strdup (buffer);
        }
    }

    printf("pp: %d\n", pp);

    for(i = 0; i != pp; i++) {
        printf("paragraphs[%d]: %s", i, paragraphs[i]);
        free(paragraphs[i]); // release memory allocated by strdup
    }
}

关于c - 数组未正确填充,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28681085/

相关文章:

java - 如何用语言对 OSI 引用模型进行编程

c - 将字符串数组分配和检索给 void 指针

arrays - 在 Swift 中修改字典数组

C 函数对两个数字数组求和?

c - 如何使用 C 捕获 FILE IO 中的错误?

c - 指针错误: Makes pointer from integer without a cast

c - 如何删除 C 中的所有赔率位?

c - 如何查找系统上是否安装了less?

java - 尝试按字母顺序对对象数组进行排序,但对象无法转换为字符串

javascript - JS 中的动态 if 语句