状态机实现文件单词统计

(Charon) 2025-04-14 17:47:54

 统计文件单词数量代码如下:

​#include <stdio.h>  // 引入标准输入输出头文件

// 定义状态常量:OUT 表示在分隔符中,IN 表示在单词中
#define OUT 0
#define IN 1
#define INIT OUT  // 初始状态设为 OUT

// 判断字符是否是分隔符(空格、换行、制表符、引号、加号、逗号、分号、句号)
int split(char c){
	if (' ' == c || '\n' == c || '\t' == c || '\"' == c || '\'' == c || '+' == c || ',' == c || ';' == c || '.' ==c)
	{
		return 1;  // 是分隔符,返回1
	}else{
		return 0;  // 不是分隔符,返回0
	}
}

// 统计文件中单词的数量
int Count_word(char *filename)
{
	int state = INIT;  // 当前状态初始化为 OUT
	int word = 0;       // 单词计数器初始化为 0
	
	FILE *fp = fopen(filename,"r");  // 以只读模式打开文件
	if (fp==NULL)
	{
		return -1;  // 打开失败,返回错误码 -1
	}

	char c;
	// 逐字符读取文件内容,直到遇到文件结束符 EOF
	while ((c = fgetc(fp)) != EOF)
	{
		if(split(c))  // 如果是分隔符
		{
			state = OUT;  // 切换为 OUT 状态
		}else if (OUT == state){  // 如果当前字符不是分隔符,且之前是 OUT 状态
			state = IN;   // 状态切换为 IN,表示进入一个新单词
			word++;       // 单词数加一
		}
	}
	
	return word;  // 返回单词数量
}

// 主函数
int main(int argc, char *argv[])
{
    if(argc <2){  // 如果没有提供文件名参数
		return -1;  // 返回错误码
	}
	
	// 调用 Count_word 统计文件中单词数量并打印
	printf("word: %d\n", Count_word(argv[1]));
}

编辑好c语言文件之后,在Linux终端输入命令进行运行

gcc -o count count.c    # 编译 count.c 为可执行文件 count

./count a.txt           # 运行程序,并统计 a.txt 文件中的单词数

-o 是 gcc 的一个选项,意思是 “output”(输出)。count 是你希望编译后生成的可执行文件的名字。会在当前目录下生成一个名为 count 的可执行文件。如果你不加 -o count,那么 GCC 默认会输出一个叫 a.out 的可执行文件。

 

一些学到的知识点:

int main(int argc, char *argv[])

 

  • argc 表示命令行参数的个数(argument count)

  • argv 是一个字符串数组(argument vector),里面每个元素是一个参数

  • argv[0] 是程序本身的名字

  • argv[1] 开始才是你运行时传的参数(例如你传一个文件名)

写法含义
char *argv[]字符串指针数组
char **argv指向字符串指针的指针,等价于上面

常用格式说明符汇总表:

格式符说明示例输出
%d打印 十进制整数(int)printf("%d", 10);10
%i%d,也用于打印整数printf("%i", 10);10
%u打印 无符号整数(unsigned int)printf("%u", 10);10
%f打印 浮点数(float, double),默认 6 位小数printf("%f", 3.14159);3.141590
%.2f打印浮点数,保留两位小数printf("%.2f", 3.14159);3.14
%c打印单个字符printf("%c", 'A');A
%s打印字符串(char 数组)printf("%s", "hello");hello
%x打印整数的 十六进制(小写)printf("%x", 255);ff
%X打印整数的 十六进制(大写)printf("%X", 255);FF
%o打印整数的 八进制表示printf("%o", 10);12
%p打印 指针地址printf("%p", ptr);0x7ffee3a4e1c8
%%打印 % 本身printf("100%%");100%

FILE 是 C 语言标准库中定义的一个 结构体类型,用于表示一个打开的文件

FILE *fp = fopen("hello.txt", "r");

如果当前目录下有一个文件叫 hello.txt,就会成功打开,fp 就是这个文件的句柄,以后你就可以用 fgetc(fp)fgets(fp) 等函数读取里面的内容了。

打开模式还有很多种

"r"只读
"w"只写(文件不存在就创建,存在就清空)
"r+"读写
"w+"写读(会清空原内容)
"a"追加
"a+"追加读写

fgetc 是 C 语言中用来从文件中按字符读取数据的函数。

int fgetc(FILE *stream);
  • 作用:从文件指针 stream 所指向的文件中读取一个字符。

  • 返回值

    • 成功:返回读取的字符(类型是 int,虽然一般用 char 接收)。因为要能区分“正常字符”和特殊值 EOF,而 char 是 8 位(0~255),不能表示 EOF(通常是 -1),所以用 int

    • 失败或到达文件结尾:返回 EOF(End Of File)。这是 C 标准库里定义的一个常量,表示“文件的结尾”。

 

int fgetc(FILE *stream);

作用:从 stream 所指向的文件中读取一个字符

HELLO WORLD

模拟程序一步步怎么读出两个单词 

步骤字符 c是否分隔符当前状态 state操作单词数 word
1'H'OUTstate → INword++1
2'E'IN无操作1
3'L'IN无操作1
4'L'IN无操作1
5'O'IN无操作1
6' 'INstate → OUT1
7'W'OUTstate → INword++2
8'O'IN无操作2
9'R'IN无操作2
10'L'IN无操作2
11'D'IN无操作2
12EOF--结束循环2

统计单词及其出现的次数

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

#define MAX_WORD_NUM 1000      // 最多能记录的单词数量
#define MAX_WORD_LEN 100       // 每个单词最大长度

// 定义结构体,用于保存一个单词和它的出现次数
struct CountWord {
    char word[MAX_WORD_LEN];  // 单词内容
    int count;                // 出现次数
};

// 判断一个字符是否为分隔符(用于切分单词)
int is_split(char c) {
    return c == ' ' || c == '\n' || c == '\t' ||
           c == ',' || c == '.' || c == ';' || c == ':' || c == '!' || c == '?';
}

// 将字符串转换为全小写,避免大小写影响比较
void to_lower(char *s) {
    for (int i = 0; s[i]; i++) {
        s[i] = tolower(s[i]);  // tolower 是将字符转换成小写
    }
}

int main(int argc, char *argv[]) {
    int word_nums = 0;  // 当前记录的单词数量
    struct CountWord word_list[MAX_WORD_NUM];  // 存储所有单词及其计数的数组
    int idx = 0;  // 当前正在拼接的单词长度
    char buffer[MAX_WORD_LEN];  // 暂存当前正在读取的单词

    // 如果命令行参数不足(没有给出文件名),返回错误
    if (argc < 2) {
        return -1;
    }

    // 打开指定文件
    FILE *fp = fopen(argv[1], "r");

    // 如果文件打开失败,返回错误
    if (fp == NULL) {
        return -1;
    }

    char c;
    // 持续读取文件中的字符,直到文件结尾
    while ((c = fgetc(fp)) != EOF) {
        if (!is_split(c)) {
            // 如果当前字符不是分隔符,就加到 buffer 中
            if (idx < MAX_WORD_LEN - 1) {
                buffer[idx++] = c;
            }
        } else {
            // 如果遇到分隔符,且 buffer 中有内容,说明一个单词读完了
            if (idx > 0) {
                buffer[idx] = '\0';     // 补上字符串结束符
                to_lower(buffer);       // 转成小写,便于统一统计
                int found = 0;

                // 遍历已有的单词列表,看看是否已经记录过这个单词
                for (int i = 0; i < word_nums; i++) {
                    if (strcmp(buffer, word_list[i].word) == 0) {
                        word_list[i].count++;  // 找到就让计数加一
                        found = 1;
                        break;  // 找到了就跳出循环,避免重复判断
                    }
                }

                // 如果没有找到这个单词,是新词
                if (!found) {
                    if (word_nums < MAX_WORD_NUM) {
                        strcpy(word_list[word_nums].word, buffer);  // 把新词加入数组
                        word_list[word_nums].count = 1;             // 初始计数为1
                        word_nums++;  // 单词总数加一
                    }
                }

                idx = 0;  // 清空 buffer,准备读取下一个单词
            }
        }
    }

    // 输出所有统计到的单词及其出现次数
    for (int i = 0; i < word_nums; i++) {
        printf("%s: %d\n", word_list[i].word, word_list[i].count);
    }

    return 0;
}

break跳出最近的一层循环

 

 

在 C 语言中,! 是逻辑“非”运算符,意思是“取反”:

  • 如果 found == 0,那 !found == 1(即 true)

  • 如果 found != 0,那 !found == 0(即 false)

 

strcmp:字符串比较

int strcmp(const char *s1, const char *s2);

作用: 比较两个字符串 s1s2 的内容。

返回值:

  • 0:如果两个字符串内容相同(注意:区分大小写);

  • < 0:如果 s1 小于 s2(按 ASCII 值逐字符比较);

  • > 0:如果 s1 大于 s2

 

strcpy:字符串拷贝

char *strcpy(char *dest, const char *src);

作用:src(源字符串)复制到 dest(目标空间),包括 \0 结尾符。

函数用途示例
strcmp比较两个字符串strcmp("a", "b") → -1
strcpy拷贝字符串内容strcpy(dest, "hello")

 

...全文
25 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

440

社区成员

发帖
与我相关
我的任务
社区描述
零声学院,目前拥有上千名C/C++开发者,我们致力将我们的学员组织起来,打造一个开发者学习交流技术的社区圈子。
nginx中间件后端 企业社区
社区管理员
  • Linux技术狂
  • Yttsam
  • 零声教育-晚晚
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

请新加入的VIP学员,先将自己参加活动的【所有文章】,同步至社区:

【内容管理】-【同步至社区-【零声开发者社区】

试试用AI创作助手写篇文章吧