前言

实现语言:Javascript

编译工具:webstorm

GitHub:https://github.com/NPjuan/WC.git

项目要求

wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。

实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。

具体功能要求:

程序处理用户需求的模式为:

wc.exe [parameter] [file_name] (由于网络原因,打包工具暂时无法下载,未能实现打包成exe文件,所以需求模式目前未能完成,只能通过GUI模式,如需使用命令行需按如下方式)

node wc.js <filePath>

其中<filePath>为可选参数,可以为相对路径或绝对路径,相对路径即相对根据wc.js所在的路径

基本功能列表:

功能 实现情况
返回文件 file.txt 的字符数) 已实现
返回文件 file.txt 的词的数目) 已实现
返回文件 file.txt 的行数 已实现
递归处理目录下符合条件的文件 已实现
返回更复杂的数据(代码行 / 空行 / 注释行) 实现注释行
调用GUI完成上述功能 已实现

空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。

代码行:本行包括多于一个字符的代码。

注释行:本行不是代码行,并且本行包括注释。一个有趣的例子是有些程序员会在单字符后面加注释:

} //注释

在这种情况下,这一行属于注释行。

解题思路

由于个人对JS语言较熟悉的原因,使用了JS来编写本题,对题目的理解为调用读取文件的api并利用正则表达式返回需要的数据,这里需要在node环境下执行,又由于需要打包成exe文件执行,则需要安装electron作为开发,可惜electron打包工具下载了N次依然下载失败,目前使用命令行的方式需要变为node wc.js <filePath>

设计实现过程

  1. 调用node.js的api来实现读取文件
  2. 先解决对单独文件的分析
  3. 使用正则表达式匹配
    1. 先去除各种标点符号作为干扰,如name,
    2. 将部分缩写变为全写,如this's --> this is
    3. 清除无意义的换行,空行
    4. 区分普通单词和在注释里的单词,将其分开
    5. 获取单词,字母,行数等等
  4. 通过递归调用,读取文件夹内的文件,再复用上述过程
  5. 对于单独文件之间输出,对于文件夹则先将数组扁平化再输出

代码说明

主要功能代码 wc.js

executor函数为主入口函数,参数可选绝对路径或相对路径或不选,返回值为文件或文件夹的详细信息

analyise函数为文本分析函数,返回文本的详细信息

ergodicDirSync为文件夹分析函数,返回值类型为数组,包含文件夹内所有符合文件类型的文本信息对象

const fs = require('fs');
const path = require('path')
// 当前目录下绝对地址
// process.argv 第一个参数为执行路径,第二个为参数
// 获取选择的路径 // 分析文件夹
function ergodicDirSync(filePath) {
try {
// 获取当前路径下的所有文件
let arr = []
let result = fs.readdirSync(filePath)
result.forEach((fileName) => {
// 获取文件的绝对地址 如果是 node_modules 则排除在外
if (fileName === 'node_modules') {
return
}
let fileDir = path.join(filePath, fileName)
let fileStatus = fs.statSync(fileDir)
// 如果是文件而非目录 // 匹配 txt js
if (fileStatus.isFile() && fileName.match(/.[txt|js]$/g)) {
arr.push(analysisFileSync(fileDir))
} else if (fileStatus.isDirectory()) {
// 递归
arr.push(ergodicDirSync(fileDir))
} else {
console.log(`${fileName} 文件格式不符合要求`)
}
})
return arr
} catch (e) {
console.warn(e)
return arr
}
}
// 分析文件
function analysisFileSync(filePath) {
let context = fs.readFileSync(filePath, 'utf-8')
let result = analysis(context)
result.filePath = filePath
return result
} // 读取文件
function readFile(filePath){
return new Promise((resolve, reject)=>{
fs.readFile(filePath, 'utf-8', (err, file) => {
if ( err ) {
reject(err)
}
resolve(file)
})
})
}
// 文件文本分析
function analysis(context) {
let time = new Date()
if (context.length === 0) {
return {
pureWords: 0,
pureLetters: 0,
words: 0,
lines: 0,
letters: 0,
notes: 0,
multilineComment: 0,
'time': new Date()-time,
code: 0
}
}
// 清除缩写,例如 Here's // 清除标点符号
let pureText = context.replace(/'s/gm, ' is').replace(/[^\s\/\*\w\u4e00-\u9fa5]/gm, '')
// 获取单词数,不含中文
let words = pureText.match(/[a-zA-Z0-9_$]+/gm)
// 获取字母
let letters = pureText.match(/\w/gm)
// 获取行数并清除无意义的换行,这里必须用到context否则如果一行文本只有标点符号会被忽略
let lines = context.split(/\r\n/gm).filter((item)=>{
return item !== ''
})
// 获取单行注释
let notes = lines.map(temp => {
if (temp.match(/\/\/[^\n]*/)) {
return temp.match(/\/\/[^\n]*/)[0]
} else {
return ''
}
}).filter((item)=>{
return item !== ''
})
// 获取多行注释 使用 *?非贪婪模式
let multilineComment = pureText.match(/(\/\*)(.|\n|\r)*?(\*\/)/gm)
// 获取注释以外的单词
// 第一个 replace 除去单行注释 //
// 第二个 replace 除去多行注释 /**/
let pureWords = pureText.replace(/\/\/[^\n]*/gm,'').replace(/(\/\*)(.|\n|\r)*?(\*\/)/gm,
'').match(/[a-zA-Z0-9_$]+/gm)
// 获取注释以外的字母
let pureLetters = pureText.replace(/\/\/[^\n]*/gm,'').replace(/(\/\*)(.|\n|\r)*?(\*\/)/gm,
'').match(/\w/gm)
return {
pureWords: pureWords?pureWords.length:0,
pureLetters: pureLetters?pureLetters.length:0,
words: words?words.length:0,
lines: lines?lines.length:0,
letters: letters?letters.length:0,
notes: notes?notes.length:0,
multilineComment: multilineComment?multilineComment.length:0,
'time': new Date()-time,
code: 1
}
} // 默认为当前路径
function executor(curFilePath = path.resolve(''), platform = 1) {
// 如果是从node.js 附带参数命令启动
if (process.argv[2] && platform) {
// 如果传递的是根路径
if (process.argv[2].match(/^[a-zA-Z]+:/g)) {
curFilePath = process.argv[2]
} else {
// 否则传递的是相对路径
curFilePath = ((curPath, dir)=>{
let pos = curPath.lastIndexOf('\\')
return path.join(curPath.slice(0, pos),dir?dir:'')
})(process.argv[1], process.argv[2])
}
console.log(curFilePath)
}
let result = undefined
let fileStatus = fs.statSync(curFilePath)
if (fileStatus.isFile()) {
console.log('你选择了文件')
result = analysisFileSync(curFilePath)
} else if (fileStatus.isDirectory()) {
console.log('你选择了文件夹')
result = ergodicDirSync(curFilePath)
}
console.log(result)
return result
}

主函数 main.js 调用GUI

let electron = require('electron')

let app = electron.app
let BrowserWindow = electron.BrowserWindow
let mainWindow = null app.on('ready', ()=>{
mainWindow = new BrowserWindow({
width: 800,
height: 800,
webPreferences: {
nodeIntegration: true
}
})
mainWindow.loadFile('./index.html')
mainWindow.on('closed', ()=>{
mainWindow = null
})
})

测试运行

命令行模式无参数模式



命令行带参数模式



GUI



GUI多文件

其中pureWords是排除在注释内的单词,pureLetters同理

项目小结

本次项目用了很多以前不曾用过或者用的很少的知识和工具,在复习和学习新技术上使用了较多的时间,所以明白了笔记的重要性,可以快速的复习,平时需要注重积累。对于新知识的学习需要明白自己最需要的是什么,单刀直入,不在一些细枝末节浪费时间,先快速开发,能用再说,后续再回头看

PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 15 15
· Estimate ·预估这个任务需要多少时间 15 15
Development 开发 550 1025
· Analysis · 需求分析(包括学习新技术) 180 210
· Design Spec · 生成设计文档 20 20
· Design Review · 设计复审(和同事审核设计文档) 0 0
· Coding Standard · 代码规范(为目前开发制定合适的规范) 0 0
· Design · 具体设计 20 40
· Coding · 具体编码 240 650
· Code Review · 代码复审 60 60
· Test · 测试(自我测试,修改代码,提交修改) 30 45
Reportin 报告 55 110
· Test Report · 测试报告 30 75
· Size Measurement · 计算工作量 15 20
· Postmortem & Process Improvement Plan · 事后总结 20 15
合计 605 1135

个人项目WC.exe Node.js+electron实现的更多相关文章

  1. Vue项目一、node.js和npm的安装和环境搭建

    一.为什么安装node.js及npm npm npm是Node.js的包管理工具(package manager),是全球最大的生态系统,同过npm可以找到很多丰富的插件来满足项目的需求. a1.现在 ...

  2. 个人项目 wc.exe

    GitHub地址:https://github.com/oAiuo/wordCount 一.题目描述 Word Count1. 实现一个简单而完整的软件工具(源程序特征统计程序).2. 进行单元测试. ...

  3. 软工个人项目 ——wc.exe

    1.GitHub项目地址 https://github.com/k8kiw/WordCount 2.PSP预计时间 PSP2.1 Personal Software Process Stages 预估 ...

  4. 记一次使用Node.js electron打包网站的记录

    具体步骤请参考:http://blog.csdn.net/a727911438/article/details/70834467 打包时出现了不少问题,逐一记录下来以供其他人参考. package.j ...

  5. 项目开发---使用node.js中sass语法

    前言:本文中所有sass文件都指后缀名为scss的文件.在此也建议使用后缀名为scss的文件,以避免sass后缀名的严格格式要求报错. 一.sass插件的安装: gulp-sass-china //  ...

  6. 个人项目-WC.exe (Java实现)

    一.Github项目地址:https://github.com/blanche789/wordCount/tree/master/src/main/java/com/blanche 二.PSP表格 P ...

  7. 软工个人项目———WC.exe(Java实现)

    一.github地址 https://github.com/hhw-15521301615/hello-world 二.PSP表格 PSP2.1 Personal Software Process S ...

  8. 使用Node.js完成的第一个项目的实践总结

    http://blog.csdn.net/yanghua_kobe/article/details/17199417 项目简介 这是一个资产管理项目,主要的目的就是实现对资产的无纸化管理.通过为每个资 ...

  9. mac下配置Node.js开发环境、express安装、创建项目

    mac下配置Node.js开发环境.express安装.创建项目 一.node.js的安装 去官网下载对应的平台版本就可以了,https://nodejs.org 二.express安装 sudo n ...

随机推荐

  1. 在npm发布自己造的轮子

    提到封装组件,发布到npm,很多同学都会觉得很神秘.但其实,npm包无非就是我们平时写的比较独立且可复用的模块.当然,想要发布,除了基础组件的编写外,还要进行一些包装.下文通过一个简单的案例,和大家一 ...

  2. PHP 超级全局变量讲解

    PHP 超级全局变量 超级全局变量在PHP 4.1.0之后被启用, 是PHP系统中自带的变量,在一个脚本的全部作用域中都可用. PHP 超级全局变量 PHP中预定义了几个超级全局变量(superglo ...

  3. Python os.ftruncate() 方法

    概述 os.ftruncate() 裁剪文件描述符fd对应的文件, 它最大不能超过文件大小.高佣联盟 www.cgewang.com Unix, Windows上可用. 语法 ftruncate()方 ...

  4. PHP pos() 函数

    实例 输出数组中的当前元素的值: <?php$people = array("Peter", "Joe", "Glenn", &quo ...

  5. PHP ezmlm_hash() 函数

    定义和用法 ezmlm_hash() 函数用于在 MySQL 数据库中保存 EZMLM 邮件列表的哈希值. 该函数接收一个 Email 地址参数,返回一个整数哈希值. 语法 int ezmlm_has ...

  6. PHP log1p() 函数

    实例 返回不同数的 log(1+number): <?phpecho(log1p(2.7183) . "<br>");echo(log1p(2) . " ...

  7. PHP strspn() 函数

    实例 返回在字符串 "Hello world!" 中包含字符 "kHlleo" 的数目: <?php高佣联盟 www.cgewang.comecho st ...

  8. P5979 [PA2014]Druzyny dp 分治 线段树 分类讨论 启发式合并

    LINK:Druzyny 这题研究了一下午 终于搞懂了. \(n^2\)的dp很容易得到. 考虑优化.又有大于的限制又有小于的限制这个非常难处理. 不过可以得到在限制人数上界的情况下能转移到的最远端点 ...

  9. luogu 3158 [CQOI2011]放棋子

    时隔多日 我又来挑战这道dp. 几个月前给写自闭了.几个月后再来. 首先一个我们能列出来的状态 是以行为转移的 f[i]表示前i行...但是会发现此时列我们控制不了 且棋子的颜色,个数我们也要放到状态 ...

  10. 认识IPv4分组

    强化一下记忆:以免忘记. 图就不放了. 首部20B (4B的整数倍) 的固定部分12个域,的确很麻烦的:IPv6才8个域,首部长度8B的整数倍 20B分5行吧,每行4B,即32位.第一行,第二行,第三 ...