C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C语言单词文本计数

利用C语言实现单词文本计数

作者:0X78

这篇文章主要为大家详细介绍了如何编写一个C语言程序,用于统计一个文本文件中每个单词出现的次数,感兴趣的小伙伴可以跟随小编一起学习一下

题目描述

任务目标:

编写一个C语言程序,用于统计一个文本文件中每个单词出现的次数(忽略大小写和标点符号),并将统计结果按单词出现次数从高到低排序后输出到另一个文件中。

输入:

源文件路径:"input.txt",该文件包含一些文本内容,单词之间由空格、标点符号等分隔。

输出:

输出文件路径:"output.txt",用于存储排序后的单词统计结果。

输出格式:每行包含一个单词及其出现次数,按出现次数从高到低排序。例如:

the: 5
is: 3
a: 2
quick: 1
brown: 1
fox: 1
jumps: 1
over: 1
lazy: 1
dog: 1

(注意:这里的输出是示例,实际输出将取决于input.txt的内容)

具体要求:

读取input.txt文件的内容。

提取文件中的单词,忽略标点符号和大小写。

统计每个单词出现的次数。

将统计结果按单词出现次数从高到低排序。

将排序后的结果写入output.txt文件。

注意事项:

单词之间的分隔符包括但不限于空格、逗号、句号、问号、感叹号等常见标点符号。

忽略标点符号和大小写进行单词统计。

输出文件中的单词及其出现次数应严格按照从高到低的顺序排列。

如果两个单词出现次数相同,它们在输出文件中的顺序可以是任意的。

示例:

假设input.txt文件内容如下:

The quick brown fox jumps over the lazy dog. The dog is a quick brown fox.

则output.txt文件内容应为:

the : 3
quick : 2
dog : 2
brown : 1
fox : 1
jumps : 1
over : 1
lazy : 1
is : 1
a : 1
animal : 1

实现提示:

可以使用C语言标准库中的文件操作函数(如fopen, fread, fclose等)来读取和写入文件。

使用字符串处理函数(如strtok, tolower, strcmp等)来处理文本内容。

使用数据结构(如哈希表或结构体数组)来存储单词及其出现次数。

使用排序算法(如快速排序或归并排序)对统计结果进行排序。

题目来源:

由文心一言生成后修改而来。

思路及部分代码

1. 获取文本中所有单词

#define C_MAX 20            // 单词的最大长度
#define SNUM_MAX 200        // 单词的最大数量(虽然这个宏在代码中未使用)
 
// 从文本文件中获取单词,并存储在二维字符数组中。
int Input_txt(char Str[][C_MAX]) {
    FILE* fp = fopen("input.txt", "r"); // 以只读模式打开文件 "input.txt"
    if (fp == NULL) {
        printf("open input.txt error\n"); // 如果文件打开失败,输出错误信息
        exit(1); // 并退出程序
    }
    
    int cnt = 0; // 单词计数器,记录已读取的单词数量
    char S[C_MAX]; // 用于临时存储从文件中读取的字符,直到遇到一个非字母字符为止
    int Si = 0; // S 数组的索引,用于记录当前正在处理的字符位置
    
    // 循环读取文件中的字符,直到到达文件末尾
    while (!feof(fp)) {
        S[Si] = fgetc(fp); // 从文件中读取一个字符,并存储到 S 数组的当前位置
        
        // 如果字符是字母(大小写均可),则进行小写转换(如果必要)
        if ((S[Si] >= 'a' && S[Si] <= 'z') || (S[Si] >= 'A' && S[Si] <= 'Z')) {
            // 如果字符是大写字母,则转换为小写字母
            if ((S[Si] >= 'A' && S[Si] <= 'Z')) S[Si] = S[Si] + 'a' - 'A';
            Si++; // 索引递增,准备存储下一个字符
        }
        else {
            // 如果字符不是字母,则将 S 数组当前位置之前的字符串视为一个单词
            S[Si] = '\0'; // 在当前位置插入字符串结束符,以标记单词的结束
            
            // 如果 S 数组的第一个字符不是字符串结束符(即已读取到至少一个字母),则将单词复制到 Str 数组中
            if (S[0] != '\0') {
                strcpy(Str[cnt], S); // 将单词复制到 Str 数组的当前位置
                cnt++; // 单词计数器递增,准备存储下一个单词
            }
            
            Si = 0; // 重置索引,准备读取下一个单词
        }
    }
    
    // 循环结束后,还需要检查 S 数组中是否还有未处理的单词(因为循环可能会在文件末尾的非字母字符处结束)
    S[Si] = '\0'; // 确保 S 数组以字符串结束符结尾
    if (S[0] != '\0') {
        strcpy(Str[cnt], S); // 如果 S 数组中有未处理的单词,则将其复制到 Str 数组的末尾
        cnt++; // 单词计数器递增
    }
    
    fclose(fp); // 关闭文件指针,释放资源(注意:原代码中缺少了这一步,建议添加)
    return cnt; // 返回读取到的单词数量
}

2. 调整单词集,去除重复部分并计数

// 去除字符串数组中的重复单词,并统计每个单词的出现次数。
// 参数:
//   char Str[][C_MAX] - 存储单词的二维字符数组。
//   int StrCnt[] - 存储每个单词出现次数的整数数组。
//   int n - 单词的总数。
// 返回值:
//   不重复单词的总数。
int Adjust_fdata(char Str[][C_MAX], int StrCnt[], int n) {
    int i = 0, cnt = 0; // i 用于遍历所有单词,cnt 用于记录不重复单词的数量。
 
    // 初始化 StrCnt 数组,将所有元素设置为 0。
    for (i = 0; i < n; i++)
        StrCnt[i] = 0;
 
    // 遍历所有单词。
    for (i = 0; i < n; i++) {
        int j = 0; // j 用于遍历当前单词之前的所有单词。
 
        // 检查当前单词是否已经在之前的单词中出现过。
        for (j = 0; j < i; j++) {
            if (strcmp(Str[i], Str[j]) == 0) // 如果找到相同的单词,则跳出内层循环。
                break;
        }
 
        // 根据 j 的值判断当前单词是否是重复的。
        if (j < i) {
            // 如果 j < i,说明当前单词在之前的单词中已经出现过,因此是重复的。
            // 更新该单词在 StrCnt 数组中的出现次数(注意:这里应该更新 j 对应的 StrCnt[j],而不是 i)。
            StrCnt[j]++;
        } else {
            // 如果 j == i,说明当前单词在之前的单词中没有出现过,因此是不重复的。
            // 将当前单词复制到 Str 数组的前 cnt 个位置(即去除重复单词后的位置)。
            strcpy(Str[cnt], Str[i]);
            // 并更新该单词在 StrCnt 数组中的出现次数为 1(之后可能会递增)。
            StrCnt[cnt]++;
            // 不重复单词数量加 1。
            cnt++;
        }
    }
 
    // 返回不重复单词的总数。
    return cnt;
}

3. 基于单词计数进行排序(此处是冒泡排序,也可采用更高效的排序算法)

// 交换两个单词及其出现次数
// 参数:
//   char Str[][C_MAX] - 存储单词的二维字符数组。
//   int StrCnt[] - 存储每个单词出现次数的整数数组。
//   int a, int b - 要交换的两个单词的索引。
void Swap(char Str[][C_MAX], int StrCnt[], int a, int b) {
    char S[C_MAX]; // 临时存储单词的字符数组。
    strcpy(S, Str[a]); // 将索引 a 处的单词复制到临时数组 S 中。
    int t = StrCnt[a]; // 临时存储索引 a 处的单词出现次数。
 
    // 将索引 b 处的单词及其出现次数复制到索引 a 处。
    StrCnt[a] = StrCnt[b];
    strcpy(Str[a], Str[b]);
 
    // 将临时存储的索引 a 处的单词及其出现次数复制到索引 b 处。
    StrCnt[b] = t;
    strcpy(Str[b], S);
}
 
// 以单词出现次数为基础,使用冒泡排序算法对单词进行排序
// 参数:
//   char Str[][C_MAX] - 存储单词的二维字符数组。
//   int StrCnt[] - 存储每个单词出现次数的整数数组。
//   int n - 单词的总数(注意:这里应该是去重后的单词总数)。
void Order_fdata(char Str[][C_MAX], int StrCnt[], int n) {
    for (int i = 0; i < n - 1; i++) { // 外层循环控制排序的轮数。
        int k = 1; // 标记本轮排序是否进行了交换,如果未进行交换则提前结束排序。
        for (int j = 1; j < n - i; j++) { // 内层循环进行相邻元素的比较和交换。
            if (StrCnt[j - 1] < StrCnt[j]) { // 如果前一个单词的出现次数小于后一个单词的出现次数,则交换它们。
                Swap(Str, StrCnt, j - 1, j); // 调用 Swap 函数进行交换。
                k = 0; // 标记本轮排序进行了交换。
            }
        }
        if (k == 1) break; // 如果本轮排序未进行交换,则说明数组已经有序,提前结束排序。
    }
    // 注意:这里的冒泡排序实现是基本正确的,但通常在实际应用中,可以使用更高效的排序算法(如快速排序、归并排序等)。
}

4. 将排序后单词写入文本

// 将单词及其出现次数输出到文本文件
// 参数:
//   char Str[][C_MAX] - 存储单词的二维字符数组。
//   int StrCnt[] - 存储每个单词出现次数的整数数组。
//   int n - 单词的总数(注意:这里应该是去重后的单词总数)。
// 返回值:
//   如果文件成功打开并写入,则返回 true;否则,在文件打开失败时退出程序。
bool Output_txt(char Str[][C_MAX], int StrCnt[], int n) {
    FILE* fp = fopen("output.txt", "w"); // 以写模式打开文件
    if (fp == NULL) {
        // 如果文件打开失败,则打印错误信息并退出程序
        perror("Failed to open file"); // 打印具体的错误信息
        exit(1); // 退出程序,返回状态码 1 表示错误
    }
 
    // 遍历单词数组,将单词及其出现次数写入文件
    for (int i = 0; i < n; i++) {
        fprintf(fp, "%s : %d\n", Str[i], StrCnt[i]); // 写入单词和次数,每个条目占一行
    }
 
    fclose(fp); // 关闭文件
 
    // 成功写入文件,返回 true
    return true;
}

5. 主函数

int main() {
    int n = 0; // 初始化单词数量为0
    char Str[SNUM_MAX][C_MAX]; // 存储单词的二维字符数组
    int StrCnt[SNUM_MAX]; // 存储每个单词出现次数的整数数组
 
    // 从文件读取单词,并返回读取到的单词数量
    n = Input_txt(Str, n);
    if (n <= 0) {
        // 如果读取失败或没有读取到单词,则打印错误信息并退出
        fprintf(stderr, "Failed to read words from input file or no words found.\n");
        return 1; // 返回非零值表示程序异常退出
    }
    printf("Input success\n"); // 打印成功信息(建议在调试时保留,但在最终产品中可能需要移除或替换为日志记录)
 
    // 调整数据,包括去重和计数单词出现次数,返回去重后的单词数量
    n = Adjust_fdata(Str, StrCnt, n);
    if (n <= 0) {
        // 如果调整数据失败(例如,输入数据有误),则打印错误信息并退出
        fprintf(stderr, "Failed to adjust data.\n");
        return 1; // 返回非零值表示程序异常退出
    }
    printf("Adjust_fdata success\n"); // 打印成功信息
 
    // 根据单词出现次数对单词进行排序
    Order_fdata(Str, StrCnt, n);
    printf("Order_fdata success\n"); // 打印成功信息
 
    // 将单词及其出现次数输出到文件
    Output_txt(Str, StrCnt, n);
    printf("Output_txt success\n"); // 打印成功信息
 
    // 程序正常结束,返回0表示成功
    return 0;
}

总代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#pragma warning(disable:4996);
 
 
 
// 获取文本中单词。
#define C_MAX 20            // 单词的最大长度
#define SNUM_MAX 200        // 单词的最大数量(虽然这个宏在代码中未使用)
 
// 从文本文件中获取单词,并存储在二维字符数组中。
int Input_txt(char Str[][C_MAX]) {
    FILE* fp = fopen("input.txt", "r"); // 以只读模式打开文件 "input.txt"
    if (fp == NULL) {
        printf("open input.txt error\n"); // 如果文件打开失败,输出错误信息
        exit(1); // 并退出程序
    }
 
    int cnt = 0; // 单词计数器,记录已读取的单词数量
    char S[C_MAX]; // 用于临时存储从文件中读取的字符,直到遇到一个非字母字符为止
    int Si = 0; // S 数组的索引,用于记录当前正在处理的字符位置
 
    // 循环读取文件中的字符,直到到达文件末尾
    while (!feof(fp)) {
        S[Si] = fgetc(fp); // 从文件中读取一个字符,并存储到 S 数组的当前位置
 
        // 如果字符是字母(大小写均可),则进行小写转换(如果必要)
        if ((S[Si] >= 'a' && S[Si] <= 'z') || (S[Si] >= 'A' && S[Si] <= 'Z')) {
            // 如果字符是大写字母,则转换为小写字母
            if ((S[Si] >= 'A' && S[Si] <= 'Z')) S[Si] = S[Si] + 'a' - 'A';
            Si++; // 索引递增,准备存储下一个字符
        }
        else {
            // 如果字符不是字母,则将 S 数组当前位置之前的字符串视为一个单词
            S[Si] = '\0'; // 在当前位置插入字符串结束符,以标记单词的结束
 
            // 如果 S 数组的第一个字符不是字符串结束符(即已读取到至少一个字母),则将单词复制到 Str 数组中
            if (S[0] != '\0') {
                strcpy(Str[cnt], S); // 将单词复制到 Str 数组的当前位置
                cnt++; // 单词计数器递增,准备存储下一个单词
            }
 
            Si = 0; // 重置索引,准备读取下一个单词
        }
    }
 
    // 循环结束后,还需要检查 S 数组中是否还有未处理的单词(因为循环可能会在文件末尾的非字母字符处结束)
    S[Si] = '\0'; // 确保 S 数组以字符串结束符结尾
    if (S[0] != '\0') {
        strcpy(Str[cnt], S); // 如果 S 数组中有未处理的单词,则将其复制到 Str 数组的末尾
        cnt++; // 单词计数器递增
    }
 
    fclose(fp); // 关闭文件指针,释放资源
    return cnt; // 返回读取到的单词数量
}
 
// 去除字符串数组中的重复单词,并统计每个单词的出现次数。
// 参数:
//   char Str[][C_MAX] - 存储单词的二维字符数组。
//   int StrCnt[] - 存储每个单词出现次数的整数数组。
//   int n - 单词的总数。
// 返回值:
//   不重复单词的总数。
int Adjust_fdata(char Str[][C_MAX], int StrCnt[], int n) {
    int i = 0, cnt = 0; // i 用于遍历所有单词,cnt 用于记录不重复单词的数量。
 
    // 初始化 StrCnt 数组,将所有元素设置为 0。
    for (i = 0; i < n; i++)
        StrCnt[i] = 0;
 
    // 遍历所有单词。
    for (i = 0; i < n; i++) {
        int j = 0; // j 用于遍历当前单词之前的所有单词。
 
        // 检查当前单词是否已经在之前的单词中出现过。
        for (j = 0; j < i; j++) {
            if (strcmp(Str[i], Str[j]) == 0) // 如果找到相同的单词,则跳出内层循环。
                break;
        }
 
        // 根据 j 的值判断当前单词是否是重复的。
        if (j < i) {
            // 如果 j < i,说明当前单词在之前的单词中已经出现过,因此是重复的。
            // 更新该单词在 StrCnt 数组中的出现次数(注意:这里应该更新 j 对应的 StrCnt[j],而不是 i)。
            StrCnt[j]++;
        }
        else {
            // 如果 j == i,说明当前单词在之前的单词中没有出现过,因此是不重复的。
            // 将当前单词复制到 Str 数组的前 cnt 个位置(即去除重复单词后的位置)。
            strcpy(Str[cnt], Str[i]);
            // 并更新该单词在 StrCnt 数组中的出现次数为 1(之后可能会递增)。
            StrCnt[cnt]++;
            // 不重复单词数量加 1。
            cnt++;
        }
    }
 
    // 返回不重复单词的总数。
    return cnt;
}
 
 
// 交换两个单词及其出现次数
// 参数:
//   char Str[][C_MAX] - 存储单词的二维字符数组。
//   int StrCnt[] - 存储每个单词出现次数的整数数组。
//   int a, int b - 要交换的两个单词的索引。
void Swap(char Str[][C_MAX], int StrCnt[], int a, int b) {
    char S[C_MAX]; // 临时存储单词的字符数组。
    strcpy(S, Str[a]); // 将索引 a 处的单词复制到临时数组 S 中。
    int t = StrCnt[a]; // 临时存储索引 a 处的单词出现次数。
 
    // 将索引 b 处的单词及其出现次数复制到索引 a 处。
    StrCnt[a] = StrCnt[b];
    strcpy(Str[a], Str[b]);
 
    // 将临时存储的索引 a 处的单词及其出现次数复制到索引 b 处。
    StrCnt[b] = t;
    strcpy(Str[b], S);
}
 
// 以单词出现次数为基础,使用冒泡排序算法对单词进行排序
// 参数:
//   char Str[][C_MAX] - 存储单词的二维字符数组。
//   int StrCnt[] - 存储每个单词出现次数的整数数组。
//   int n - 单词的总数(注意:这里应该是去重后的单词总数)。
void Order_fdata(char Str[][C_MAX], int StrCnt[], int n) {
    for (int i = 0; i < n - 1; i++) { // 外层循环控制排序的轮数。
        int k = 1; // 标记本轮排序是否进行了交换,如果未进行交换则提前结束排序。
        for (int j = 1; j < n - i; j++) { // 内层循环进行相邻元素的比较和交换。
            if (StrCnt[j - 1] < StrCnt[j]) { // 如果前一个单词的出现次数小于后一个单词的出现次数,则交换它们。
                Swap(Str, StrCnt, j - 1, j); // 调用 Swap 函数进行交换。
                k = 0; // 标记本轮排序进行了交换。
            }
        }
        if (k == 1) break; // 如果本轮排序未进行交换,则说明数组已经有序,提前结束排序。
    }
    // 注意:这里的冒泡排序实现是基本正确的,但通常在实际应用中,可以使用更高效的排序算法(如快速排序、归并排序等)。
}
 
// 将单词及其出现次数输出到文本文件
// 参数:
//   char Str[][C_MAX] - 存储单词的二维字符数组。
//   int StrCnt[] - 存储每个单词出现次数的整数数组。
//   int n - 单词的总数(注意:这里应该是去重后的单词总数)。
// 返回值:
//   如果文件成功打开并写入,则返回 true;否则,在文件打开失败时退出程序。
bool Output_txt(char Str[][C_MAX], int StrCnt[], int n) {
    FILE* fp = fopen("output.txt", "w"); // 以写模式打开文件
    if (fp == NULL) {
        // 如果文件打开失败,则打印错误信息并退出程序
        perror("Failed to open file"); // 打印具体的错误信息
        exit(1); // 退出程序,返回状态码 1 表示错误
    }
 
    // 遍历单词数组,将单词及其出现次数写入文件
    for (int i = 0; i < n; i++) {
        fprintf(fp, "%s : %d\n", Str[i], StrCnt[i]); // 写入单词和次数,每个条目占一行
    }
 
    fclose(fp); // 关闭文件
 
    // 成功写入文件,返回 true
    return true;
}
 
int main() {
    int n = 0; // 初始化单词数量为0
    char Str[SNUM_MAX][C_MAX]; // 存储单词的二维字符数组
    int StrCnt[SNUM_MAX]; // 存储每个单词出现次数的整数数组
 
    // 从文件读取单词,并返回读取到的单词数量
    n = Input_txt(Str, n);
    if (n <= 0) {
        // 如果读取失败或没有读取到单词,则打印错误信息并退出
        fprintf(stderr, "Failed to read words from input file or no words found.\n");
        return 1; // 返回非零值表示程序异常退出
    }
    printf("Input success\n"); // 打印成功信息(建议在调试时保留,但在最终产品中可能需要移除或替换为日志记录)
 
    // 调整数据,包括去重和计数单词出现次数,返回去重后的单词数量
    n = Adjust_fdata(Str, StrCnt, n);
    if (n <= 0) {
        // 如果调整数据失败(例如,输入数据有误),则打印错误信息并退出
        fprintf(stderr, "Failed to adjust data.\n");
        return 1; // 返回非零值表示程序异常退出
    }
    printf("Adjust_fdata success\n"); // 打印成功信息
 
    // 根据单词出现次数对单词进行排序
    Order_fdata(Str, StrCnt, n);
    printf("Order_fdata success\n"); // 打印成功信息
 
    // 将单词及其出现次数输出到文件
    Output_txt(Str, StrCnt, n);
    printf("Output_txt success\n"); // 打印成功信息
 
    // 程序正常结束,返回0表示成功
    return 0;
}

改进建议

1. 优化去重与计数算法:可以考虑使用哈希表(在 C 语言中可以使用结构体和链表等数据结构实现)来存储单词及其出现次数,以提高去重和计数的效率。

2. 使用更高效的排序算法:可以考虑使用快速排序、归并排序等更高效的排序算法来替换冒泡排序。

3. 增强错误处理能力:在处理文件操作时,可以考虑使用更灵活的错误处理方式(如返回错误代码、抛出异常等),以便在出现错误时能够更好地控制程序的流程。

4. 代码优化与重构:可以对代码进行进一步的优化和重构,以提高代码的可读性和可维护性。例如,可以将一些重复的代码封装成函数或宏等。

到此这篇关于利用C语言实现单词文本计数的文章就介绍到这了,更多相关C语言单词文本计数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文