一、程序概述
1.1 功能描述
实现类似Linux wc命令的文件统计工具,能够统计:
-
字符数(包括所有字符)
-
单词数(基于空白字符分隔)
-
行数(基于换行符\n计数)
1.2 程序结构
主函数 main()
↓
处理命令行参数
↓
调用 file_statistics()
↓
打开文件 → 失败 → 错误处理
↓
循环读取字符
↓
统计行数、字符数、单词数
↓
关闭文件
↓
输出结果
1.3代码
cpp
#include <stdio.h>
#include <string.h>
// 统计文件行数、字符数、单词数
int file_statistics(const char *filename)
{
FILE *fp = fopen(filename, "r");
if (!fp) {
perror("打开文件失败");
return -1;
}
int lines = 0, chars = 0, words = 0;
int ch, prev_ch = ' ';
while ((ch = fgetc(fp)) != EOF) {
chars++;
if (ch == '\n') {
lines++;
}
// 简单的单词计数:当前字符是空格/换行,前一个字符不是空格/换行
if ((ch == ' ' || ch == '\n' || ch == '\t') &&
(prev_ch != ' ' && prev_ch != '\n' && prev_ch != '\t')) {
words++;
}
prev_ch = ch;
}
// 处理最后一个单词
if (prev_ch != ' ' && prev_ch != '\n' && prev_ch != '\t' && chars > 0) {
words++;
}
printf("文件统计信息:\n");
printf(" 行数: %d\n", lines + (chars > 0 ? 1 : 0));
printf(" 字符数: %d\n", chars);
printf(" 单词数: %d\n", words);
fclose(fp);
return 0;
}
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("用法: %s <文件名>\n", argv[0]);
return -1;
}
return file_statistics(argv[1]);
}
二、核心算法分析
2.1 行数统计
// 简单直接的方法
if (ch == '\n') {
lines++;
}
特殊情况处理:
-
最后一行没有换行符时,需要额外+1
-
空文件应该显示0行
2.2 原始单词统计算法
// 边界检测算法
if ((ch == ' ' || ch == '\n' || ch == '\t') &&
(prev_ch != ' ' && prev_ch != '\n' && prev_ch != '\t')) {
words++;
}
算法原理:
文本: "Hello World!\n"
流程:
-
当前字符 ' ',前一个字符 'o' → 检测到单词边界 → words=1
-
当前字符 '\n',前一个字符 '!' → 检测到单词边界 → words=2
2.3 改进的单词统计算法
#include <ctype.h>
// 状态机算法
int in_word = 0;
while ((ch = fgetc(fp)) != EOF) {
if (isalnum(ch) || ch == '_' || ch == '-') {
in_word = 1; // 进入单词状态
} else if (isspace(ch) || ispunct(ch)) {
if (in_word) {
words++; // 离开单词状态
in_word = 0;
}
}
}
if (in_word) words++; // 处理最后一个单词
单词定义:
-
字母、数字、下划线、连字符视为单词字符
-
空白字符和标点符号作为单词分隔符
三、关键代码实现细节
3.1 文件打开与错误处理
FILE *fp = fopen(filename, "r");
if (!fp) {
perror("打开文件失败"); // 自动输出错误信息
return -1;
}
3.2 字符读取循环
int ch; // 必须用int,不能用char!
while ((ch = fgetc(fp)) != EOF) {
// 处理字符
}
为什么用int不用char?
-
fgetc()返回int类型,范围是0-255和EOF(-1)
-
如果使用char,无法区分EOF和某些字符
3.3 处理最后一行
// 调整行数计数
printf("行数: %d\n", lines + (chars > 0 ? 1 : 0));
逻辑解释:
-
情况1: "Hello\nWorld" → lines=1, chars>0 → 显示2行 ✓
-
情况2: "" → lines=0, chars=0 → 显示0行 ✓
-
情况3: "Hello" → lines=0, chars>0 → 显示1行 ✓
四、程序扩展功能
4.1 多文件支持实现
int total_lines = 0, total_chars = 0, total_words = 0;
for (int i = 1; i < argc; i++) {
int lines, chars, words;
if (process_file(argv[i], &lines, &chars, &words) == 0) {
// 累加统计
total_lines += lines;
total_chars += chars;
total_words += words;
}
}
4.2 性能优化与计时
#include <time.h>
clock_t start = clock();
// ... 文件处理代码 ...
clock_t end = clock();
double time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("处理时间: %.3f秒\n", time_used);
4.3 格式化输出
// 对齐输出格式
printf("%-20s 行数: %6d 单词数: %6d 字符数: %6d\n",
filename, lines, words, chars);
// 输出示例:
// test.txt 行数: 3 单词数: 9 字符数: 48
五、学习要点总结
-
文件操作基础:fopen、fgetc、fclose的正确使用
-
错误处理:检查返回值,使用perror输出错误
-
状态机思想:单词统计中的状态转换
-
命令行参数:argc/argv的使用
-
性能考虑:缓冲区、计时、大文件处理
-
可扩展性:设计支持功能扩展的程序结构