扩展我之前的练习,我有一个文本文件,每行填充一个单词。
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)。我仍然试图完全理解混合指针和结构,并说任何想法,评论,抱怨?
答案 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的内存分配。无论哪种方式都很好,这在很大程度上取决于你。