引言
在软件开发过程中,代码统计是一项基础但重要的工作,它能帮助我们了解项目规模、代码质量和维护复杂度。本文将详细介绍一个高效的多线程C代码统计工具的实现原理和技术细节。
一、工具概述
这个C语言实现的统计工具能够递归遍历指定目录,分析所有.c和.h文件,提供以下统计信息:
(1)文件总数和目录总数
(2)总代码行数
(3)有效代码行数及占比
(4)注释行数及占比
(5)空白行数及占比
二、核心数据结构设计
2.1 统计数据结构(Count)
typedef struct {
long long total_lines; // 总行数
long long code_lines; // 代码行数
long long comment_lines; // 注释行数
long long blank_lines; // 空行数
int file_count; // 文件数量
int dir_count; // 目录数量
pthread_mutex_t lock; // 互斥锁
} Count;
这个结构体使用long long
类型存储行数统计,确保大代码库不会溢出,同时包含互斥锁保证多线程安全。
2.2 线程参数结构(ThreadArg)
typedef struct {
Count *count; // 共享统计结构指针
char path[1024]; // 文件路径
} ThreadArg;
三、关键技术实现
3.1 多线程架构
工具采用主从式(Master-Worker)多线程模型:
(1)主线程负责目录遍历和任务分发
(2)工作线程负责实际的文件行数统计
// 创建工作线程
pthread_t *tid = malloc(2000 * sizeof(pthread_t));
ThreadArg *t_a = malloc(sizeof(ThreadArg));
strncpy(t_a->path, path, sizeof(t_a->path));
t_a->count = count;
pthread_create(&tid[n], NULL, count_line, t_a);
3.2 线程同步机制
pthread_mutex_lock(&t_a->count->lock);
t_a->count->total_lines += lines;
// 其他统计量更新...
pthread_mutex_unlock(&t_a->count->lock);
3.3 递归目录遍历
采用深度优先搜索(DFS)算法递归处理子目录:
if (d->d_type == 4) { // 目录
count->dir_count++;
count_file(path, count); // 递归调用
}
3.4 代码行分析算法
文件行数统计实现了精细的代码分析:
while(fgets(buffer, size, fp)) {
// 预处理行内容
trim_whitespace(buffer);
if(/* 空行判断 */) {
blank_lines++;
}
else if(/* 多行注释开始 */) {
comment_lines++;
in_comment = 1;
}
// 其他情况处理...
}
四、性能优化策略
(1)并行处理:每个文件独立线程处理,充分利用多核CPU
(2)批量线程管理:预分配线程ID数组,统一等待线程结束
(3)轻量级同步:细粒度锁只保护必要共享数据
(4)内存高效:及时释放不再需要的资源
五、完整代码实现
#include<stdio.h>
#include<stdlib.h>
#include<dirent.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/stat.h>
#include<string.h>
#include<ctype.h>
typedef struct{
long long total_lines;
long long code_lines;
long long comment_lines;
long long blank_lines;
int file_count;
int dir_count;
pthread_mutex_t lock;
}Count;
typedef struct{
Count *count;
char path[1024];
}ThreadArg;
//计算文件的总行数
void *count_line(void *arg) {
ThreadArg *t_a = (ThreadArg *)arg;
FILE *fp = fopen(t_a->path, "r");
if (!fp) {
perror("文件打开失败");
free(t_a);
return NULL;
}
int size=4096;
char buffer[4096];
long long code_line=0,empty_line=0,explant_line=0;
int in_comment=0;
long long lines = 0;
while(fgets(buffer, size, fp) != NULL) { // 逐行读取文件
lines++; // 总行数+1
// 去除行首空白
char *p = buffer;
while(*p == ' ' || *p == '\t') p++;
// 去除行尾空白
char *end = p + strlen(p) - 1;
while(end >= p && (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r')) {
end--;
}
*(end+1) = '\0';
// 判断行类型
//注释行中的空行也算作空行
if(*p == '\0') {
empty_line++;
}
else if(strstr(p, "/*")) {
explant_line++;
in_comment = 1;
if(strstr(p, "*/")) in_comment = 0;
}
else if(in_comment) {
explant_line++;
if(strstr(p, "*/")) in_comment = 0;
}
else if(p[0] == '/' && p[1] == '/') {
explant_line++;
}
else {
code_line++;
}
}
fclose(fp);
pthread_mutex_lock(&t_a->count->lock);
t_a->count->total_lines += lines;
t_a->count->code_lines += code_line;
t_a->count->comment_lines += explant_line;
t_a->count->blank_lines += empty_line;
pthread_mutex_unlock(&t_a->count->lock);
free(t_a);
return NULL;
}
// 检查是否为C源文件或头文件
int is_c_file(const char *name) {
const char *ext = strrchr(name, '.');
if (!ext) return 0;
return (strcmp(ext, ".c") == 0) || (strcmp(ext, ".h") == 0);
}
//计算文件数和目录数
void count_file(char *name, Count *count) {
pthread_t *tid=malloc(2000*sizeof(pthread_t));
int n=0;
DIR *dir = opendir(name);
if (!dir) {
perror("无法打开目录");
return;
}
struct dirent *d;
char path[1024];
while ((d = readdir(dir)) != NULL) {
if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
continue;
}
snprintf(path, sizeof(path), "%s/%s", name, d->d_name);
if (d->d_type==8) {
if (is_c_file(d->d_name)) {
// 创建线程统计行数
ThreadArg *t_a = malloc(sizeof(ThreadArg));
strncpy(t_a->path, path, sizeof(t_a->path));
t_a->count=count;
pthread_create(&tid[n], NULL, count_line, t_a);
n++;
}
pthread_mutex_lock(&count->lock);
count->file_count++;
pthread_mutex_unlock(&count->lock);
} else if (d->d_type==4) {
pthread_mutex_lock(&count->lock);
count->dir_count++;
pthread_mutex_unlock(&count->lock);
count_file(path, count);
}
}
for(int i=0;i<n;i++){
pthread_join(tid[i],NULL);
}
free(tid);
closedir(dir);
}
int main(int argc, char const *argv[])
{
Count count={0,0,0,0,0,1};
char *name="linux-6.15.6"; //文件名
count_file(name,&count);
printf("文件数为:%d\n",count.file_count);
printf("文件夹数为:%d\n",count.dir_count);
printf("文件总行数为:%lld\n",count.total_lines);
printf("代码行数为:%lld (%.2f%%)\n", count.code_lines,
(double)count.code_lines * 100 / count.total_lines);
printf("注释行数为:%lld (%.2f%%)\n", count.comment_lines,
(double)count.comment_lines * 100 / count.total_lines);
printf("空行数为:%lld (%.2f%%)\n", count.blank_lines,
(double)count.blank_lines * 100 / count.total_lines);
pthread_mutex_destroy(&count.lock);
return 0;
}
有更高效的方法欢迎指导