1. Github项目地址
2.1 WC 项目要求
wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,
给出某程序设计语言源文件的字符数、单词数和行数。
实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他
扩展功能,并能够快速地处理多个文件。
具体功能要求:
程序处理用户需求的模式为:
wc.exe [parameter] [file_name]
基本功能列表:
wc.exe -c file.c //返回文件 file.c 的字符数
wc.exe -w file.c //返回文件 file.c 的词的数目
wc.exe -l file.c //返回文件 file.c 的行数
扩展功能:
-s 递归处理目录下符合条件的文件。
-a 返回更复杂的数据(代码行 / 空行 / 注释行)。
空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。
代码行:本行包括多于一个字符的代码。
注释行:本行不是代码行,并且本行包括注释。一个有趣的例子是有些程序员会在单字符后面加注释:
} //注释
在这种情况下,这一行属于注释行。
[file_name] : 文件或目录名,可以处理一般通配符。
高级功能:
-x 参数。这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。
2.2 实现程序前,模块开发预计时间
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 30 | |
Estimate | 估计这个任务需要多少时间 | 30 | |
Development | 开发 | 800 | |
Analysis | 需求分析(包括学习新技术) | 60 | |
Design Spec | 生成设计文档 | 20 | |
Design Review | 设计复审 | ||
Coding Standard | 代码规范 | 20 | |
Design | 具体设计 | 30 | |
Coding | 具体编码 | 300 | |
Code Review | 代码复审 | 20 | |
Test | 测试(自我测试、修改代码、提交修改) | 200 | |
Reporting | 报告 | 60 | |
Test Report | 测试报告 | 30 | |
Size Measurement | 计算工作量 | 10 | |
Postmortem&Process Improvement Plan | 事后总结,并提出过程改进计划 | 30 | |
Total | 合计 | 1610 |
3. 解题思路
编程语言的选择
最近在学习python,觉得python比较方便好用,内含的库较多,可直接使用,因此选择python进行这次作业的开发。
需要用到的知识
控制命令行传参数、tkinter、字典、I/O流、正则表达式
结构分析
看到有较多的功能,所以不能将所有的程序写在一个py文件,要将必要的功能进行封装,需要时再调用,所以只需要一个主程序,去调用其他字程序就行了。
遇到的困难
有时候总是把程序写死,不够灵活,解决起来费时间,命令行传参不是很懂,开始时出错,python的图形界面也是刚开始学,编码特别慢,现学现做,效果不是很好,还有就是不知道如何打开资源管理器,花了很多时间。
4. 设计实现过程
根据需求,对每个功能进行细分、简化,找出每个功能的详细需求,把每个功能都实现完成,生成函数,然后考虑封装调用,在主函数中实现调用各个功能函数,完成开发。
函数调用
5. 代码说明
1)主程序
import sys import output as op from window import Window def main(): """主程序""" file = sys.argv[2] #从命令行中获取参数文件名 command = sys.argv[1] file_name = file if command == '-x': Window() op.Output(command,file_name) main()
2)Output()函数:根据命令行的参数对应输出或者调用
def Output(command,file_name):
"""选择命令,输出数据"""
datas = count(file_name)
if command == '-c':
export(file_name,"字符数",datas['character'])
elif command == '-w':
export(file_name, "单词数", datas['word'])
elif command == '-l':
export(file_name, "行数", datas['line'])
elif command == '-s': #处理目录下符合条件的文件
print(" C:\Users\荣\PycharmProjects\homework目录下的文件统计: ")
recursion()
elif command == '-a':
print(' 文件%s下的统计:' % file_name)
print(datas)
else:
print("请输入正确指令")
def export(filename,name,data):
print('文件名:%s, %s: %d' % (filename,name,data))
3)count()函数,实现统计的功能,计算行数、字符数、代码数,利用字典存储数据,返回。
简要说明:首先利用open函数打开文件,然后对文件中每行进行循环,如果去掉每行两端的空白后等于空,则空行数加1;如果这行含有“//”,则注释行加1;计算单词数的时候,先用空格代替其他特殊符号,利用split()函数通过空格使每行分隔开成为一个列表,使用正则判断是否为单词,如果是,则单词数加1。
def count(file_name):
"""统计文件中的数据"""
data = {}
data['line'] = 0
data['word'] = 0
data['character'] = 0
data['empty_line'] = 0 #空行
data['note_line'] = 0 #注释行
data['code_line'] = 0 #代码行
try:
with open(file_name, 'r', encoding='utf-8')as fi:
for lines in fi:
if lines.strip() == '':
data['empty_line'] += 1
continue
if '//' in lines:
data['note_line'] += 1
replace = re.subn(r"[#<>,;()""&{}+]"," ",lines) #用空格代替替他符号
words = replace[0].split()
data['line'] += 1
for word in words:
if re.match(r".*[A-Za-z].*",word):
data['word'] += 1
if data['word'] > 1:
data['code_line'] += 1
data['character'] += len(lines) - 1
except Exception as err:
print(err)
finally:
fi.close()
return data
4)recursion()函数,调用getfile()函数,获得文件名,利用循环处理目录下符合条件的文件,输出数据。
def recursion(): """处理目录下符合条件的文件""" file_list = getfile() for filename in file_list: datalist = count(filename) print("文件名:%s" % filename) print(datalist)
5)getfile()函数,利用os的功能walk找到目录下符合条件的所有文件。
def getfile(): """处理目录下符合条件的文件,返回文件名""" g = os.walk(r"C:Users荣PycharmProjectshomework") files = [] for path,d,filelist in g: for filename in filelist: if filename.endswith('.c'): files.append(filename) return files
6)window窗口,当命令行含有-x的时候,调用此窗口,选择文件进行统计。
def Window(): """打开窗口,选择文件""" root = tk.Tk() root.title("选择文件") root.geometry("300x100") button = tk.Button(root, text='浏览文件', relief=tk.RAISED,command=Response) button.place(x=100, y=20, width=100, height=50) root.mainloop() sys.exit()
7)Response()函数,当点击浏览文件按钮后,弹出文件管理窗口,选择文件。
def Response(): '''打开选择文件夹对话框''' root = tk.Tk() root.withdraw() Filepath = filedialog.askopenfilename() #获得选择好的文件 # print('Folderpath:',Folderpath) print('Filepath:',Filepath) data_list = out.count(Filepath) print(data_list) Result(data_list,Filepath)
8)Result()函数,利用tkinter的窗口显示结果,现学现做,略显生硬。
def Result(datas,filename):
"""统计结果窗口"""
root = tk.Tk()
root.title("选择文件")
root.geometry("420x320")
file_name = tk.Label(root, text='文件名: %s'%filename, justify=tk.RIGHT, width=80)
line = tk.Label(root, text='行数: %s'%datas['line'], justify=tk.RIGHT, width=80)
word = tk.Label(root, text='单词数: %s'%datas['word'], justify=tk.RIGHT, width=80)
character = tk.Label(root, text='字符数: %s' % datas['character'], justify=tk.RIGHT, width=80)
empty_line = tk.Label(root, text='空行数: %s' % datas['empty_line'], justify=tk.RIGHT, width=80)
note_line = tk.Label(root, text='注释行: %s' % datas['note_line'], justify=tk.RIGHT, width=80)
code_line = tk.Label(root, text='代码行: %s' % datas['code_line'], justify=tk.RIGHT, width=80)
file_name.place(x=50,y=10,width=280,height=30)
line.place(x=135, y=40, width=100, height=20)
word.place(x=135, y=85, width=100, height=20)
character.place(x=135, y=130, width=100, height=20)
empty_line.place(x=135, y=175, width=100, height=20)
note_line.place(x=135, y=220, width=100, height=20)
code_line.place(x=135, y=265, width=100, height=20)
root.mainloop()
sys.exit()
6. 测试运行
测试文件:
测试结果:
7. 实现完程序后,实际花费时间
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 30 | 30 |
Estimate | 估计这个任务需要多少时间 | 30 | 50 |
Development | 开发 | 800 | 1000 |
Analysis | 需求分析(包括学习新技术) | 60 | 120 |
Design Spec | 生成设计文档 | 20 | 20 |
Design Review | 设计复审 | ||
Coding Standard | 代码规范 | 20 | 20 |
Design | 具体设计 | 30 | 30 |
Coding | 具体编码 | 300 | 400 |
Code Review | 代码复审 | 20 | 30 |
Test | 测试(自我测试、修改代码、提交修改) | 200 | 200 |
Reporting | 报告 | 60 | 60 |
Test Report | 测试报告 | 30 | 30 |
Size Measurement | 计算工作量 | 10 | 10 |
Postmortem&Process Improvement Plan | 事后总结,并提出过程改进计划 | 30 | 30 |
Total | 合计 | 1610 | 2030 |
8. 项目小结
经过这个小项目开发,发现的问题还是挺多的,编程基础很重要,有很多功能函数其实已经存在的了,只是不知道,或者不会调用,然后自己编码进行解决,吃力不讨好的事情,费时间,而且代码拓展性不高。有些功能看起来很难实现,但细细分析,把程序分为一个个函数,把每个函数的功能写好,再供程序调用,先解决小的问题,把简单的弄好,困难的部分也会变得简单。由于时间和基础的关系,有些功能没完全实现,或者是没实现,所以说PSP表格很重要,对自己的时间规划进行预估,给自己压力,每一次缩小了实际耗时和预估耗时的差距,就是进步。此次作业存在许多不足,以后会加强改进。
2020-03-15 23:57:00