计算文本文件

时间:2017-04-17 04:08:12

标签: c arrays pointers struct

扩展我之前的练习,我有一个文本文件,每行填充一个单词。

hello
hi
hello
bonjour
bonjour
hello

当我从文件中读取这些单词时,我想将它们与结构指针数组(从文本文件创建)进行比较。如果数组中不存在该单词,则应将该单词存储到计数为1的结构指针中。如果该单词已存在于数组中,则计数应增加1.我将结果写入新文件(那已经存在了。)

hello = 3
hi = 1
bonjour = 2 

这是我的代码

#include <stdio.h>
#include <stdlib.h>
struct wordfreq{
    int count;
    char *word;
};
int main(int argc, char * argv[]) {
    struct wordfreq *words[1000] = {NULL};
    int i, j, f = 0;
    for(i=0; i <1000; i++)
        words[i] = (struct wordfreq*)malloc(sizeof(struct wordfreq));

    FILE *input = fopen(argv[1], "r");
    FILE *output = fopen(argv[2], "w");
    if(input == NULL){
        printf("Error! Can't open file.\n");
        exit(0);
    }

    char str[20];
    i=0;
    while(fscanf(input, "%s[^\n]", &str) ==1){
        //fprintf(output, "%s:\n", str);
        for(j=0; j<i; j++){
            //fprintf(output, "\t%s  == %s\n", str, words[j] -> word);
            if(str == words[j]->word){
                words[j] ->count ++;
                f = 1;
            }
        }
        if(f==0){
            words[i]->word = str;
            words[i]->count = 1;
        }
        //fprintf(output, "\t%s = %d\n", words[i]->word, words[i]->count);
        i++;
    }

    for(j=0; j< i; j++)
        fprintf(output, "%s = %d\n", words[j]->word, words[j]->count);
    for(i=0; i<1000; i++){
        free(words[i]);
    }
    return 0;
}

我使用了几个fprintf语句来查看我的值,我可以看到str是正确的,当我在横向(str == words[I]->word)期间到达比较str与其他数组结构指针words[0] -> word的行时始终与str相同,words[i]->words的其余部分为(null)。我仍然试图完全理解混合指针和结构,并说任何想法,评论,抱怨?

1 个答案:

答案 0 :(得分:1)

你可能会使事情变得比必要的更难,而且你肯定会在输入文件的情况下分配997多于必要的结构。无需预先分配所有1000结构。 (你可以自由地这样做,这只是一个内存管理问题)。关键是每次遇到一个唯一的单词时,你只需要分配一个新的结构。 (对于您的数据文件,为3次)。对于所有其他情况,您只需更新count即可添加已存储的字词的匹配项。

此外,如果没有令人信服的理由使用struct,那么使用 pointers-to-char 数组作为指向每个{{1}的指针同样容易然后是一个简单的word数组作为int [1000](或频率)数组。你的选择。对于两个数组,您只需为每个唯一count分配,并且永远不需要为每个word分配单独的分配。

将这些部分组合在一起,您可以将代码(不包括文件 - 可以通过简单的重定向处理)减少到以下内容:

struct

示例输入文件

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

enum { MAXC = 128, MAXW = 1000 };

struct wordfreq{
    int count;
    char *word;
};

int main (void) {

    struct wordfreq *words[MAXW] = {0};
    char tmp[MAXC] = "";
    int n = 0;

    /* while < MAXW unique words, read each word in file */
    while (n < MAXW && fscanf (stdin, " %s", tmp) == 1) {
        int i;

        for (i = 0; i < n; i++)         /* check against exising words */
            if (strcmp (words[i]->word, tmp) == 0)  /* if exists, break */
                break;

        if (i < n) {            /* if exists */
            words[i]->count++;  /* update frequency */
            continue;           /* get next word */
        }

        /* new word found, allocate struct and
         * allocate storage for word (+ space for nul-byte) 
         */
        words[n] = malloc (sizeof *words[n]);
        words[n]->word = malloc (strlen (tmp) + 1);
        if (!words[n] || !words[n]->word) { /* validate ALL allocations */
            fprintf (stderr, "error: memory exhausted, words[%d].\n", n);
            break;
        }
        words[n]->count = 0;    /* initialize count */

        strcpy (words[n]->word, tmp); /* copy new word to words[n] */
        words[n]->count++;            /* update frequency to 1 */
        n++;                          /* increment word count */
    }

    for (int i = 0; i < n; i++) { /* for each word */
        printf ("%s = %d\n", words[i]->word, words[i]->count);
        free (words[i]->word);    /* free memory when no longer needed */
        free (words[i]);
    }

    return 0;
}

示例使用/输出

$ cat dat/wordfile.txt
hello
hi
hello
bonjour
bonjour
hello

与动态分配内存的任何代码一样,您需要验证对内存的使用,以确保您没有超出边界或基于条件移动或跳过未初始化的值。在Linux中,$ ./bin/filewordfreq <dat/wordfile.txt hello = 3 hi = 1 bonjour = 2 是自然的选择(每个操作系统都有类似的程序)。只需通过它运行程序,例如:

valgrind

验证您$ valgrind ./bin/filewordfreqstruct <dat/wordfile.txt ==2000== Memcheck, a memory error detector ==2000== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. ==2000== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info ==2000== Command: ./bin/filewordfreqstruct ==2000== hello = 3 hi = 1 bonjour = 2 ==2000== ==2000== HEAP SUMMARY: ==2000== in use at exit: 0 bytes in 0 blocks ==2000== total heap usage: 6 allocs, 6 frees, 65 bytes allocated ==2000== ==2000== All heap blocks were freed -- no leaks are possible ==2000== ==2000== For counts of detected and suppressed errors, rerun with: -v ==2000== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) 分配的所有内存以及没有内存错误。

仔细看看,如果您有任何其他问题,请告诉我。

使用2阵列代替 free

如上所述,有时使用存储阵列频率数组可以简化完成同样的事情。无论何时你需要任何“设置”的频率,你的第一个想法应该是频率数组。它只不过是与“set”中的项目数量相同的数组(在开头时初始化为struct)。同样的方法适用于在存储阵列中添加(或查找现有副本)元素时,将频率数组中的相应元素增加{{1 }}。完成后,频率数组元素将保持存储阵列中相应元素出现的频率。

这是上述程序的等效内容。

0

使用这种方法,通过为1使用静态声明的频率数组,可以消除1/2的内存分配。无论哪种方式都很好,这在很大程度上取决于你。