Linux C编程:统计文件单词数量&统计每个单词出现的次数

目录

前言

一、完整代码展示

二、代码解释

1.结构体定义与基本设置

2.辅助函数

3.核心处理逻辑

3.1 count_words()

3.2 main() 函数

三、实际运行结果展示

1.输入文件c.txt的内容

2.运行结果

3.运行结果分析&可以改进的地方

总结


前言

这是零声学院Linux入门环境编程的第二个项目,原本是统计文件单词数量作为例题,统计每个单词出现的次数作为课后作业。其中统计文件单词数量是用有限状态机来实现的,原理比较简单,且我认为源码存在一定缺陷,因此不介绍状态机的方法。下面我直接推倒重来,将两个问题合并起来解决。

一、完整代码展示

代码如下:  

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

#define MAX_WORDS 1000   // 最大单词数
#define MAX_WORD_LEN 100  // 每个单词的最大长度

// 定义一个结构体用于存储单词及其出现次数
typedef struct {
    char word[MAX_WORD_LEN];
    int count;
} WordCount;

int is_word_char(char c) {
    return isalpha(c) || c == '-' || c == '\'';  // 判断是否是字母、连字符或撇号
}

int is_valid_hyphen_or_apostrophe(char *word, int pos, char c) {
    // 处理连字符和撇号不能在单词开头或结尾,且不能连续出现
    if ((c == '-' || c == '\'') && (pos == 0 || !isalpha(word[pos - 1]))) {
        return 0;  // 连字符或撇号在开头或者前面不是字母则无效
    }
    return 1;
}

void count_words(char *filename) {
    WordCount words[MAX_WORDS];  // 存储单词及其出现次数
    int word_count = 0;          // 当前已存储的单词数
    int total_word_count = 0;    // 总单词数

    FILE *fp = fopen(filename, "r");
    if (fp == NULL) {
        perror("Failed to open file");
        return;
    }

    char word[MAX_WORD_LEN];
    char c;
    int pos = 0;
    while ((c = fgetc(fp)) != EOF) {
        if (is_word_char(c)) {
            if (is_valid_hyphen_or_apostrophe(word, pos, c)) {
                word[pos++] = tolower(c);  // 构建单词,允许合法的连字符或撇号
            }
        } else if (pos > 0) {
            word[pos] = '\0';  // 终止当前单词
            pos = 0;

            // 查找单词是否已经存在
            int index = -1;
            for (int i = 0; i < word_count; i++) {
                if (strcmp(words[i].word, word) == 0) {
                    index = i;
                    break;
                }
            }

            if (index != -1) {
                words[index].count++;  // 如果单词已存在,增加计数
            } else {
                // 如果单词不存在,添加到数组中
                strcpy(words[word_count].word, word);
                words[word_count].count = 1;
                word_count++;
            }

            total_word_count++;  // 增加总单词计数

            if (word_count >= MAX_WORDS) {
                printf("Too many words!\n");
                break;
            }
        }
    }

    fclose(fp);

    // 输出每个单词及其出现次数
    for (int i = 0; i < word_count; i++) {
        printf("%s: %d\n", words[i].word, words[i].count);
    }

    // 输出总单词数
    printf("Total word count: %d\n", total_word_count);
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("Usage: %s <filename>\n", argv[0]);
        return -1;
    }

    count_words(argv[1]);
    return 0;
}

二、代码解释

这段代码的功能是读取指定的文本文件,统计每个单词出现的次数,并输出总单词数和每个单词的出现次数。代码主要分为以下几个部分:

1.结构体定义与基本设置

#define MAX_WORDS 1000
#define MAX_WORD_LEN 100

typedef struct {
    char word[MAX_WORD_LEN];
    int count;
} WordCount;
  • MAX_WORDS:定义了最多可以处理 1000 个不同的单词。
  • MAX_WORD_LEN:单词的最大长度为 100 个字符。
  • WordCount 结构体用来保存每个单词及其出现的次数。

2.辅助函数

3.核心处理逻辑

3.1 count_words()

这个函数是代码的核心,负责文件读取和单词统计。

void count_words(char *filename) {
    WordCount words[MAX_WORDS];
    int word_count = 0;
    int total_word_count = 0;

    FILE *fp = fopen(filename, "r");
    if (fp == NULL) {
        perror("Failed to open file");
        return;
    }

    char word[MAX_WORD_LEN];
    char c;
    int pos = 0;

    while ((c = fgetc(fp)) != EOF) {
        if (is_word_char(c)) {
            if (is_valid_hyphen_or_apostrophe(word, pos, c)) {
                word[pos++] = tolower(c);
            }
        } else if (pos > 0) {
            word[pos] = '\0';  // 单词结束
            pos = 0;
            
            // 查找单词是否已经存在
            int index = -1;
            for (int i = 0; i < word_count; i++) {
                if (strcmp(words[i].word, word) == 0) {
                    index = i;
                    break;
                }
            }

            if (index != -1) {
                words[index].count++;  // 如果单词已存在,增加计数
            } else {
                strcpy(words[word_count].word, word);  // 新单词
                words[word_count].count = 1;
                word_count++;
            }

            total_word_count++;
            
            if (word_count >= MAX_WORDS) {
                printf("Too many words!\n");
                break;
            }
        }
    }

    fclose(fp);

    // 输出每个单词及其出现次数
    for (int i = 0; i < word_count; i++) {
        printf("%s: %d\n", words[i].word, words[i].count);
    }

    // 输出总单词数
    printf("Total word count: %d\n", total_word_count);
}
 

3.2 main() 函数

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("Usage: %s <filename>\n", argv[0]);
        return -1;
    }

    count_words(argv[1]);
    return 0;
}
 

  • 该函数检查命令行参数是否包含文件名,如果没有文件名作为参数,输出用法说明并返回错误。如果有文件名,则调用 count_words 函数对文件进行处理。

三、实际运行结果展示

1.输入文件c.txt的内容

hello world it's mother-in-law John's cat. For exa-
mple, to report an error in datagram processing. 
example it is in law.
 

2.运行结果

3.运行结果分析&可以改进的地方

代码在大部分情况下可以正确识别文件中的单词并完成统计,但存在以下问题。

  • 连字符处理需要增强:目前连字符的处理仅允许它出现在单词内部,如果连字符出现在行尾,并且应该连接下一行的单词,则会出现错误,如上图展示的exa-,mple。
  • 's被视为前一个单词的一部分:这导致统计并不完全准确,'s和is没有被当做同一个单词处理


总结

  • 这段代码跳出了状态机的解法,借用链表来记录和存储各个单词出现的次数,以及统计所有单词的数量,并且输出。避开了状态机方法中对if()内条件的穷举——这是一个重大的缺陷,除非预设输入的文本除了字母以外其他字符的种类很少,不然很容易出现遗漏。
  • 这段代码不能分辨's和换行用连字符连接起来的单词,但除此之外功能是比较完整的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值