c程序读取文件并计算数组中指定的单词

c program to read file and count words specified in array

我正在尝试读取一个包含段落的文件,计算特定单词出现的次数(我指定并存储在数组中的单词),然后将该结果打印到另一个看起来像,

systems, 2
computer, 3
programming, 6

等等。目前,这段代码所做的只是吐出段落中的每个单词及其各自的计数。任何帮助将不胜感激。

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

int main()
{
    FILE* in;
    FILE* out;

    char arr1[13][100] = { "systems", "programming", "computer", "applications", "language", "machine"};
    int arr2[180] = {0};
    int count = 0;
    char temp[150];

    in = fopen("out2.dat", "r");
    out = fopen("out3.dat", "w");

    while (fscanf(in, "%s", temp) != EOF)
    {
        int i, check = 8;
        for (i = 0;i < count;i++)
        {
            if (strcmp(temp, arr1[i]) == 0)
            {
                arr2[i]++;
                check = 1;
                break;
            }
        }
        if (check == 1) continue;
        strcpy(arr1[count], temp);
        arr2[count++] = 1;
    }
    int i;
    for (i = 0; i < count; i++)
        fprintf(out, "%s, %d\n", arr1[i], arr2[i]);

    return 0;
}

在整个程序中使用 count 没有多大意义。

声明为int count = 0;,然后作为本次循环的上界

for (i = 0; i < count; i++)

限制使用哪些搜索词。这也意味着这个循环不会在周围 while 循环的第一次迭代时进入。

因此,check != 1,所以此后count用作arr1中的索引,当前读取的“单词”将被复制到

strcpy(arr1[count], temp);

这完全没有意义。为什么要覆盖您正在搜索的数据?

然后count被用来设置arr2的第一个元素为1后增加到1

while 循环的第二次迭代中,for 循环将 运行 进行一次迭代,比较新读取的“单词” (temp)针对 arr1 的第一个元素(现在是读取的最后一个“单词”)。

如果匹配:arr2 中的第一个元素从 1 递增到 2,跳过字符串复制,并且 count 不递增。

如果不匹配,则将新的“单词”复制到arr1的第二个元素中,arr2的第二个元素设置为1count 增加到 2.

从这里开始失控。

鉴于上面显示的输入,当 count 达到 13 时,这将访问 arr1 out-of-bounds。

对于数据选择较少的文件(<= 13 个唯一“单词”,长度 < 100),这可能会意外地“起作用”,方法是用文件中的单词填充 arr1。这将具有向您显示输入文件中每个“单词”的计数的最终效果。


最终,当发生以下情况之一时,您将调用 Undefined Behavior

  • fscanf(in, "%s", temp) 读取溢出 temp 缓冲区的字符串。
  • count 超出了 arr1arr2 的界限。
  • strcpy(arr1[count], temp);arr1.
  • 中复制溢出缓冲区的字符串
  • 要么fopen失败。

除了不安全之外,fscanf(in, "%s", temp) 会将空格以外的任何内容视为有效字符串的一部分。这包括尾随标点符号,这可能是也可能不是问题,具体取决于您要匹配的标记(systems.systems)。您可能需要更强大的解析。

无论如何,要么创建一个由搜索词和频率组成的结构数组,要么创建两个长度相同的数组来表示此数据:

const char *words[6] = { "systems", "programming", "computer", "applications", "language", "machine"};
unsigned freq[6] = { 0 };

不需要复制任何东西。记得检查 fopen 是否失败,并在读取时限制 %s 以免溢出输入缓冲区。

程序的其余部分看起来很相似:针对所有搜索词测试每个输入“词”;如果匹配则增加相应的频率。

使用结构数组的示例:

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

int main(void) {
    struct {
        const char *word;
        unsigned freq;
    } search_words[] = {
        { "systems", 0 },
        { "programming", 0 },
        { "computer", 0 },
        { "applications", 0 },
        { "language", 0 },
        { "machine", 0 }
    };

    size_t length = sizeof search_words / sizeof *search_words;

    FILE *input_file = fopen("out2.dat", "r");
    FILE *output_file = fopen("out3.dat", "w");

    if (!input_file || !output_file) {
        fclose(input_file);
        fclose(output_file);
        fprintf(stderr, "Could not access files.\n");
        return 1;
    }

    char word[256];

    while (1 == fscanf(input_file, "%255s", word))
        for (size_t i = 0; i < length; i++)
            if (0 == strcmp(word, search_words[i].word))
                search_words[i].freq++;

    fclose(input_file);

    for (size_t i = 0; i < length; i++)
        fprintf(output_file, "%s, %u\n",
                search_words[i].word,
                search_words[i].freq);

    fclose(output_file);
}

cat out3.dat:

systems, 1
programming, 1
computer, 2
applications, 2
language, 1
machine, 1