因为最近毕业设计要写答辩ppt,里面需要统计工作量,因此写了一个用来统计代码行数的小demo。
主要实现思路是先遍历一个文件夹下的所有文件,然后挑选出所有的文件,返回一个按文件格式划分的对象,使用这个对象就可以统计各种类型文件的行数了。
感觉基于那个文件对象还可以搞一些类似文本分析等功能,以后有时间就做。
除此之外,类里面还有一些静态方法例如直接统计某个文本的行数(使用换行符分割)、获取文件夹所有文件等小功能也可以拆出来用。
仓库地址
具体使用
先实例化一个对象,然后指定需要忽略的文件格式和文件夹,以下面的代码为例:
先把这段代码放在项目的根目录下,指定当前文件夹作为起始搜索路径,指定vue、md、js、py格式,忽略node_modules和项目打出来的包。
具体输出如下:
打印的第一个是所有的文件名,第二个是指定格式文件的总行数。
{
vue: [
'D:\\code\\design-frontend\\src\\App.vue',
'D:\\code\\design-frontend\\src\\pages\\ForgetPassword.vue',
'D:\\code\\design-frontend\\src\\pages\\Login.vue',
'D:\\code\\design-frontend\\src\\pages\\Main.vue',
'D:\\code\\design-frontend\\src\\pages\\SignUp.vue',
'D:\\code\\design-frontend\\src\\pages\\Test.vue',
'D:\\code\\design-frontend\\src\\components\\desc\\DescFunction.vue',
'D:\\code\\design-frontend\\src\\components\\desc\\DescTask.vue',
'D:\\code\\design-frontend\\src\\components\\personal\\revoke.vue',
'D:\\code\\design-frontend\\src\\components\\personal\\UpdateInfo.vue',
'D:\\code\\design-frontend\\src\\components\\personal\\UpdatePassword.vue',
'D:\\code\\design-frontend\\src\\components\\task\\TaskList.vue',
'D:\\code\\design-frontend\\src\\components\\task\\TaskUpload.vue',
'D:\\code\\design-frontend\\src\\components\\visual\\DirectedGraph.vue',
'D:\\code\\design-frontend\\src\\components\\visual\\TopologicalGraph.vue',
'D:\\code\\design-frontend\\src\\components\\visual\\VisualGragh.vue',
'D:\\code\\design-frontend\\src\\components\\visual\\VisualTree.vue',
'D:\\code\\design-frontend\\src\\components\\widget\\CodeButton.vue',
'D:\\code\\design-frontend\\src\\components\\widget\\Markdown.vue'
],
js: [
'D:\\code\\design-frontend\\.eslintrc.js',
'D:\\code\\design-frontend\\.postcssrc.js',
'D:\\code\\design-frontend\\LineReader.js',
'D:\\code\\design-frontend\\vue.config.js',
'D:\\code\\design-frontend\\build\\build.js',
'D:\\code\\design-frontend\\build\\check-versions.js',
'D:\\code\\design-frontend\\build\\utils.js',
'D:\\code\\design-frontend\\build\\vue-loader.conf.js',
'D:\\code\\design-frontend\\build\\webpack.base.conf.js',
'D:\\code\\design-frontend\\build\\webpack.dev.conf.js',
'D:\\code\\design-frontend\\build\\webpack.prod.conf.js',
'D:\\code\\design-frontend\\config\\dev.env.js',
'D:\\code\\design-frontend\\config\\index.js',
'D:\\code\\design-frontend\\config\\prod.env.js',
'D:\\code\\design-frontend\\src\\main.js',
'D:\\code\\design-frontend\\src\\antdOptimize\\icons.js',
'D:\\code\\design-frontend\\src\\apis\\apis.js',
'D:\\code\\design-frontend\\src\\apis\\request.js',
'D:\\code\\design-frontend\\src\\router\\index.js',
'D:\\code\\design-frontend\\src\\store\\index.js',
'D:\\code\\design-frontend\\src\\utils\\encrypt.js',
'D:\\code\\design-frontend\\src\\utils\\utils.js'
],
py: [],
md: [
'D:\\code\\design-frontend\\README.md',
'D:\\code\\design-frontend\\src\\docs\\ForgetPasswordTips.md',
'D:\\code\\design-frontend\\src\\docs\\revokeTips.md',
'D:\\code\\design-frontend\\src\\docs\\SignUpTips.md',
'D:\\code\\design-frontend\\src\\docs\\SystemDesc.md',
'D:\\code\\design-frontend\\src\\docs\\UpdatePasswordTips.md',
'D:\\code\\design-frontend\\src\\docs\\UploadDataDesc.md',
'D:\\code\\design-frontend\\src\\docs\\UploadInfoTips.md',
'D:\\code\\design-frontend\\src\\docs\\VisualTreeExample.md',
'D:\\code\\design-frontend\\src\\docs\\VisualTreeTips.md'
]
}
{ vue: 2336, js: 1606, py: 0, md: 125 }
const fs = require("fs");
const path = require("path");
class LineReader {
/**
* 创建一个LineReader实例
* @param {*} path
* @param {*} filetypes
* @param {*} ignoreddir
*/
constructor(path, filetypes, ignoreddir) {
this.path = path;
this.filetypes = filetypes;
this.ignoreddir = ignoreddir;
this.dirstructure = LineReader.getDirectoryStructure(
this.path,
this.filetypes,
this.ignoreddir
);
}
/**
* 对于一个指定的文本类文件返回行数
* @param {*} filepath
* @returns
*/
static checkSingleFileLine(filepath) {
const filecontent = fs.readFileSync(filepath, "utf-8");
return filecontent.split("\n").length;
}
/**
* 判断一个路径是否为文件夹
* @param {*} path
* @returns
*/
static isDir(path) {
return fs.lstatSync(path).isDirectory();
}
/**
* 获取一个文件夹的所有文件,并以对象的形式返回
* @param {*} pathName
* @param {*} suffixes
* @param {*} ignoreddir
* @returns 按文件格式对象为键的对象
*/
static getDirectoryStructure = function(pathName, suffixes, ignoreddir) {
// 定义一个队列,用于遍历整个文件夹
let arr = [pathName];
// 定义最终的结果
let files = {};
// 按照文件格式向结果对象中的每个文件格式添加一个空数组
for (let i = 0; i < suffixes.length; i++) {
files[suffixes[i]] = [];
}
// 对文件夹进行DFS
while (arr.length) {
// 取出队首
let curpath = arr.shift();
if (LineReader.isDir(curpath)) {
// 当路径为文件夹时
const curdir = fs.readdirSync(curpath);
for (let i = 0; i < curdir.length; i++) {
const newpath = path.join(curpath, curdir[i]);
if (ignoreddir.indexOf(newpath) < 0) {
// 判断是否为需要忽略的文件夹,若不是则入队
arr.push(newpath);
}
}
} else {
// 当路径为文件时,拆分文件名并统计格式
const temp = curpath.split(".");
const cursuffix = temp[temp.length - 1];
const index = suffixes.indexOf(cursuffix);
if (index > -1)
// 若为指定格式则添加至结果对象的所属格式中
files[suffixes[index]].push(curpath);
}
}
return files;
};
/**
* 获取linereader实例的所有行数
* @returns 以文件格式为键名的对象
*/
getLineStatistics() {
// 取出已经计算好的文件夹内容
const dir = this.dirstructure;
// 取出所有文件格式
let keys = Object.keys(dir);
let linenums = {};
for (let i = 0; i < keys.length; i++) {
// 获取当前应当遍历的文件格式
const typename = keys[i];
// 取出当前文件格式对应的所有文件
const someTypeFiles = dir[typename];
// 初始化当前文件格式的行数为0
linenums[typename] = 0;
for (let j = 0; j < someTypeFiles.length; j++) {
// 加上当前文件的行数
linenums[typename] += LineReader.checkSingleFileLine(someTypeFiles[j]);
}
}
return linenums;
}
}
/** 测试 */
const reader = new LineReader(
path.resolve("./"),
["vue", "txt", "js", "py"],
[
path.join(path.resolve("./"), "node_modules"),
path.join(path.resolve("./"), "dist")
]
);
console.log(reader.dirstructure);
console.log(reader.getLineStatistics());