Python模拟wc命令(软件测试第二次作业)
Python实现字符,单词,行,代码行,空行及可视化
Gitee项目地址:https://gitee.com/biubiubiuLYQ/word_and_character_statistics
一.解题思路
一开始拿到该题目,心想最近在学Shell编程,好像写个脚本,用wc命令都可以较轻松的把这些功能实现,但是这好像要得是具体去模拟wc命令,让自己更了解是如何实现的,一想,基本功能都挺好实现的,就是从没实现过带命令参数的程序,也不知道py文件打包成exe文件该如何实现,于是百度了一下,发现实现方法还挺多,那还等什么,于是便开干了......
二.程序设计实现过程
1.基本功能:
写了三个函数,一个函数only_one()是参数只有文件,我默认将字符数,单词数,行数写进与wc.exe文件下的同目录下的result.txt文件里,第二个函数是include_many_minglin(file_path,out_file)判断带命令(-w,-l,-c,-o)参数的,还未写扩展功能时,我也只给定一个参数,首先验证所带参数是否合法(命令重复或命令参数无法识别),验证成功之后继续验证是否带“-o”参数,因为两种情况不一样,如若不带“-o”参数,则所读取的文件为参数的最后一个,而带“-o”参数,则所读取的文件为倒数第三个,最后一个命令参数为结果写进的文件。第三个函数是out_nominglin(file_path)用来接受输出结果指定文件,默认为wc.exe当前目录下的result.txt文件。
2.扩展功能:
扩展功能都是基于基本功能实现的,重写了include_many_minglin(file_path,out_file,stop_txt_file=''),首先添加了判断命令是否带“-a”,若带该参数,则也保存代码行,空行,注释行,然后添加了停止词参数然后判断是否带命令参数“-e”,若有则读取停止词,将所读文件中的单词把停止词排除然后计数,还添加了函数getallfile(path),若命令参数中含“-s”,则遍历当前目录及子目录获取所有文件,另外一个函数是tomgpei(re_file),用于匹配文件名是否满足条件,并提取。
2.高级功能:
这是我单独做的模块,用的GUI,添加了一个类App(),该类主要用来显示可视框并实现点击按钮选择文件并显示文件的字符数,单词数,行数等信息,判断命令参数只有一个且为“-x”时,实列化类并执行。
三.代码说明
1.命令参数只有文件时的函数:
获取文件名,然后读取文件,获取字符数,单词数(用正则模块以“,”和空格分割),行数(以"\n"分割)并写进文件(file_path我配置为与wc.py同目录下的result.txt)。
def only_one():
'''
无命令参数时,写入文件单词,字符,行数
:return:
'''
file_path = sys.argv[-1]
try:
with open(file_path, 'r') as fp:
all_text = fp.read()
zifu = len(all_text)
words = len(re.split('[\s,]', all_text))
hangs = len(all_text.split('\n'))
with open(result_path, 'a', encoding='utf-8') as fp2:
fp2.write(sys.argv[-1] + ',' + '字符数:' + str(zifu) + '\n')
fp2.write(sys.argv[-1] + ',' + '单词数:' + str(words) + '\n')
fp2.write(sys.argv[-1] + ',' + '行数:' + str(hangs) + '\n') fp.close()
except:
print('参数为一个时只能是文件或文件路径!' + '请检查参数是否正确或文件是否正确!')
2.含命令参数('-c','-l','-w','-a'):
首先在主函数验证过后,命令参数合法才调用该函数,out_file为输出结果的文件(默认为result.txt),stop_txt_path代表停止词文件路径,如果命令参数带"-o"且命令格式正确,,若stop_txt_path不为空则读取该停止词文件,以逗号分割成若干个字符串,然后读取要读取的文件,验证是否有单词与停止词文件中字符串相同,若有则全部移除,最后将移除停止词后的文件单词计数。其他"-l",'"-w","-c"判断也是命令合法后然后若命令包含它,则在文件中保存相关信息。
def include_many_minglin(file_path, out_file=result_path, stop_txt_path=''):
'''
包含多个参数时:
file_path:被读取文件的路径
out_file:输出结果输入的文件路径
:param file_path:
:param out_file:
:return:
'''
try:
with open(file_path, 'r') as fp:
all_text = fp.read()
all_minglin = sys.argv[1:-1]
if '-c' in all_minglin:
zifu = len(all_text.strip())
try:
with open(out_file, 'a', encoding='utf-8') as fp2:
fp2.write(file_path + ',字符数:' + str(zifu) + '\n')
fp2.close()
except:
print('输出文件打开或创建失败!')
exit()
# print('字符数:' + str(zifu))
if '-w' in all_minglin:
words = re.split('[\s,,]', all_text)
try:
if stop_txt_path:
try:
with open(stop_txt_path, 'r') as stops:
stop_words = stops.read().split()
with open(out_file, 'a', encoding='utf-8') as fp2:
for word in words:
if word in stop_words:
words.remove(word)
else:
pass
fp2.write(file_path + ',单词数:' + str(len(words)) + '\n')
fp2.close()
stops.close()
except:
print('停止词文件打开失败!!')
else:
with open(out_file, 'a', encoding='utf-8') as fp2:
fp2.write(file_path + ',单词数:' + str(len(words)) + '\n')
fp2.close()
except:
print('输出文件打开或创建失败!')
exit()
if '-l' in all_minglin:
hangs = len(all_text.split('\n'))
try:
with open(out_file, 'a', encoding='utf-8') as fp2:
fp2.write(file_path + ',行数:' + str(hangs) + '\n')
fp2.close()
except:
print('输出文件打开或创建失败!')
exit()
# print('行数:' + str(hangs))
if '-a' in all_minglin:
control_data = ['%', '-', 'm.n', 'l', 'h']
hangss = all_text.split('\n')
null_ = True
code = 0
nulls = 0
zhushi = 0
for hang in hangss:
if len(hang) > 1:
for every_data in hang:
if every_data in control_data:
pass
else:
null_ = False
break
if null_ == True or hang == '{' or hang == '}' or len(hang) == 0:
nulls += 1
else:
if '//' in hang or '/*' in hang:
zhushi += 1
else:
code += 1
with open(out_file, 'a', encoding='utf-8') as fp2:
fp2.write(file_path + ',' + '代码行/空行/注释行:' + str(code) + '/' + str(nulls) + '/' + str(zhushi) + '\n')
fp2.close()
fp.close()
except:
print('你的文件路径或文件格式错误,无法打开该文件,请检查后再次输入!!!')
3.遍历所在目录及子目录获取所有文件并筛选符合条件的:
若命令参数带"-s",首先通过getallfile获取当前目录及子目录所有文件,然后通过tomgpei验证符合条件的文件,若参数本身是一个目录,则默认获取该目录及子目录下的所有文件并返回,否则(我这里写的通配符只写了*),及以*分割参数,然后判断文件名是否符合条件(包含以*分割的所有字符串)。
def getallfile(path):
'''
递归获取目录下所有文件
:param path:
:return:
'''
allfilelist = os.listdir(path)
for file in allfilelist:
filepath = os.path.join(path, file)
# 判断是不是文件夹
if os.path.isdir(filepath):
getallfile(filepath)
else:
allfile.append(filepath)
return allfile def tomgpei(re_file):
'''
通配符匹配
:param re_file:
:return:
'''
if os.path.exists(re_file):
if os.path.isdir(re_file):
allfiles = getallfile(re_file)
return allfiles
elif os.path.isfile(re_file):
all = []
all.append(re_file)
return all
else:
print('不是文件名或目录!')
return None
elif '*' in re_file:
all_txts = []
allfiles = getallfile(Now_Dir)
split_datas = re_file.split('*')
for file in allfiles:
y_n = True
for split_data in split_datas:
if split_data in file:
pass
else:
y_n = False
if y_n == True:
all_txts.append(file)
else:
pass
return all_txts
else:
print('目录或文件不存在!')
return None
4.可视化实现:
这里是GUI的一些简单用法,具体就是添加了两个按钮(一个退出,一个选择文件),并绑定了相应的函数,一个是退出按钮自带的关闭弹出框的函数frame.quit,选择文件按钮绑定的是chose_wenjian函数,用于选择文件并对文件相关信息统计然后动态加入txt文本框中。
class App:
'''
gui实现可视化
''' def __init__(self, master):
# 构造函数里传入一个父组件(master),创建一个Frame组件并显示
frame = Frame(master)
frame.pack()
# 创建两个button,并作为frame的一部分
self.wenjian_path = StringVar()
self.label = Label(text="文件路径: ")
self.label.pack(side=TOP)
self.entry = Entry(textvariable=self.wenjian_path)
self.entry.pack(side=TOP)
self.button = Button(frame, text="退出", fg="red", command=frame.quit, anchor='sw')
self.button.pack(side=RIGHT) # 此处side为LEFT表示将其放置 到frame剩余空间的最左方
self.hi_there = Button(frame, text="选择文件", fg='red', command=self.chose_wenjian, anchor='se')
self.hi_there.pack(side=LEFT)
self.label = Label(text="文件信息显示: ")
self.label.pack(side=TOP)
self.txt = Text(width=55, height=15)
self.txt.pack() def chose_wenjian(self):
paths = askopenfile()
self.wenjian_path.set(paths.name)
if paths:
self.txt.delete(0.0, tkinter.END)
try:
with open(paths.name, 'r') as f:
all_contents = f.read()
zifu = len(all_contents)
words = len(re.split('[\s,]', all_contents))
hangs = len(all_contents.split('\n'))
control_data = ['%', '-', 'm.n', 'l', 'h']
null_ = True
code = 0
nulls = 0
zhushi = 0
hangss = all_contents.split('\n')
for hang in hangss:
if len(hang) > 1:
for every_data in hang:
if every_data in control_data:
pass
else:
null_ = False
break
if null_ == True or hang == '{' or hang == '}' or len(hang) == 0:
nulls += 1
else:
if '//' in hang or '/*' in hang:
zhushi += 1
else:
code += 1
self.txt.insert(END, "字符数:" + str(zifu) + "\n")
self.txt.insert(END, "单词数:" + str(words) + "\n")
self.txt.insert(END, "行数:" + str(hangs) + "\n")
self.txt.insert(END, "代码行数:" + str(code) + "\n")
self.txt.insert(END, "空行数:" + str(nulls) + "\n")
self.txt.insert(END, "注释行数:" + str(zhushi) + "\n") except:
self.txt.insert(END, "文件打开失败,请检查文件格式是否正确!!!")
else:
pass
5.判断命令参数是否合法,并获取相关信息:
判断参数是否存在,是否重复,是否含“-s”,“-o”,"-e",若含有则修改相应状态(False/True),并在后续调用不同的函数。
# 默认无结果输出文件
data_out_txt = False
# 默认为停止词文件
data_stop_txt = False
#是否含需要遍历目录
DIGUI = False
if len(sys.argv) <= 1:
print('命令格式不正确!!!')
elif len(sys.argv) == 2:
if sys.argv[-1] == '-x':
win = Tk()
win.geometry('500x310+500+200')
# 设置窗口标题
win.title('文件检索')
app = App(win)
win.mainloop()
else:
only_one()
else:
minglin = ['-c', '-w', '-l', '-o', '-s', '-a', '-e']
for i in sys.argv[1:-1]:
if i not in minglin and sys.argv[sys.argv.index(i) - 1] == '-o':
pass
elif i not in minglin and sys.argv[sys.argv.index(i) - 1] == '-e':
pass
elif i not in minglin and "-s" in sys.argv[1:sys.argv.index(i)]:
pass
else:
if i in minglin and sys.argv.count(i) == 1 and i != '-o' and i != '-e' and i!='-s':
pass
elif i in minglin and sys.argv.count(i) > 1:
print('命令重复!!请修改!')
# break
exit()
elif sys.argv.count(i) == 1 and i == '-o':
data_out_txt = True
elif sys.argv.count(i) == 1 and i == '-e':
data_stop_txt = True
stop_txt_path = sys.argv[sys.argv.index(i) + 1]
elif sys.argv.count(i) == 1 and i == '-s':
DIGUI = True
else:
print('命令格式错误!!!')
# break
exit()
四.测试设计过程(路径覆盖)
1.基本功能测试:
1.1返回字符数:wc.exe -c G:\main.c
1.2返回单词数:wc.exe -w G:\main.c
1.3返回行数:wc.exe -l G:\main.c
1.4返回字符数和单词数:wc.exe -c -w G:\main.c
1.5返回字符数和行数:wc.exe -c -l G:\main.c
1.6返回单词数和行数:wc.exe -w -l G:\main.c
1.7返回字符数,单词数,行数:wc.exe -c -w -l G:\main.c

所有测试命令

结果
1.8测试输出结果到指定文件(字符数):wc.exe -c G:\main.c -o outfile.txt
1.9测试输出结果到指定文件(单词数):wc.exe -w G:\main.c -o outfile.txt
1.10测试输出结果到指定文件(行数):wc.exe -l G:\main.c -o outfile.txt
1.11测试输出结果到指定文件(字符数和单词数):wc.exe -c -w G:\main.c -o outfile.txt
1.12测试输出结果到指定文件(字符数和行数):wc.exe -c -l G:\main.c -o outfile.txt
1.13测试输出结果到指定文件(单词数和行数):wc.exe -w -l G:\main.c -o outfile.txt
1.14测试输出结果到指定文件(单词数,字符数,行数):wc.exe -c -w -l G:\main.c -o outfile.txt

测试命令

结果
2.扩展功能测试:
2.1遍历目录及子目录符合条件的文件(输出字符数):wc.exe -s -c *.txt

测试命令

结果
2.2遍历目录及子目录符合条件的文件(输出字符数及单词数):wc.exe -s -c -w *.txt

测试命令

结果
2.3遍历目录及子目录符合条件的文件(输出字符数,单词数,行数):wc.exe -s -c -w -l *.txt

测试命令

结果
2.4遍历目录及子目录符合条件的文件(输出字符数,单词数,行数并到指定文件):wc.exe -s -c -w -l *.txt -o G:/all_results.txt

测试用例

结果
2.4处理复杂的代码行(只返回代码行,空行,注释行):

测试用例

结果
2.5返回字符数,单词数,行数,复杂数据:

测试用例

结果
2.6 返回字符数,单词数,行数,复杂数据到指定文件:
测试用例

输出结果
2.7遍历目录及子目录下的满足条件的文件,并输出复杂数据:
测试用例

结果
2.8遍历目录及子目录下的满足条件的文件,并输出复杂数据到指定文件:

测试用例

结果
2.9遍历目录及子目录下的满足条件的文件,并输出复杂数据,字符数,单词数,行数:

测试用例

结果
2.10遍历目录及子目录下的满足条件的文件,并输出复杂数据,字符数,单词数,行数到指定文件:

测试用例

结果
2.11排除停止词输出单词数:

测试命令

结果验证正确
2.12返回当前目录及子目录中所有.txt文件的字符数、单词总数、代码行数、空行数、注释行数,并将结果保存在output.txt中,且统计单词时忽略stop.txt中的单词:

测试命令

结果
3.高级功能测试:

测试命令

弹出框

选择文件后显示信息

选择其他文件会覆盖上一文件信息
五.目前的问题:
1.打包成exe文件后程序运行比Python环境下运行py文件慢很多;
2.打包成exe文件后默认的result.txt文件不会创建在于wc.exe文件下的同目录中,而是创建在当前所在文件夹下,而运行原py文件会创建在于wc.py文件下的同目录中;
3.若在遍历目录下文件时,把输出结果文件创建在当前目录下会阻塞。
六.参考文献
Tkiner的简单使用:http://www.runoob.com/python/python-gui-tkinter.html
Python打包成exe文件:https://blog.csdn.net/u010812071/article/details/78507946
Python模拟wc命令(软件测试第二次作业)的更多相关文章
- 逐步实现python版wc命令
Python 如何处理管道输入输出 sys.stdin 等于打开了一个文件对象,所有输入的文件都会写入到标准输入文件中(键盘) sys.stdout 等于打来了一个文件对象,使用.write()把信息 ...
- 使用 python 实现 wc 命令程序的基本功能
这里使用了 python 的基本代码实现了 Linux 系统下 wc 命令程序的基本功能. #!/usr/bin/env python #encoding: utf-8 # Author: liwei ...
- 作业(二)—python实现wc命令
Gitee地址:https://gitee.com/c1e4r/word-count(为什么老师不让我们用github) 0x00 前言 好久没发博客了,感觉自己的学习是有点偷懒了.这篇博客也是应专业 ...
- 软件测试第二周作业 WordCount
本人github地址: https://github.com/wenthehandsome23 psp阶段 预估耗时 (分钟) 实际耗时 (分钟) 计划 30 10 估计这个任务需要多少时间 20 ...
- 软件测试第二次作业——Fault,Failure,Error辨析与设计测试用例
Fault 静态错误 ,Failure 外部错误 ,Error 内部错误 问题答案 第一题 1.1 当数组x内的元素≥2时,该循环不会检测到x[0]这个元素. 1.2 test: x=[2, 3, 2 ...
- 软件测试第二次作业:初识JUNIT单元测试方法
软件测试有很多分类,从测试的方法上可分为:黑盒测试.白盒测试.静态测试.动态测试 从软件开发的过程分为:单元测试.集成测试.确认测试.验收.回归等. 在众多的分类中,与开发人员关系最紧密的莫过于单 ...
- 2003031121-浦娟-python数据分析第四周作业-第二次作业
项目 内容 课程班级博客链接 20级数据班(本) 作业链接 Python第四周作业第二次作业 博客名称 2003031121-浦娟-python数据分析第四周作业-matolotlib的应用 要求 每 ...
- python学习:简单的wc命令实现
#!/usr/bin/python import sys import os try: fn = sys.argv[1] except IndexError: print &q ...
- 第二课作业——redis常用命令
第二课时作业 静哥 by 2016.2.23~2016.2.22 [作业描述] 1.key string list hash结构中,每个至少完成5个命令,包含插入 修改 删除 查询,list 和h ...
随机推荐
- WPF 格式化输出- IValueConverter接口的使用 datagrid列中的值转换显示
以前在用ASP.NET 做B/S系统时,可以方便地在GRIDVIEW DATAList等数据控件中,使用自定义的代码逻辑,比如 使用 <%# GetBalance(custID) %> 这 ...
- 根据JavaBean创建数据库的操作SQL
根据JavaBean创建数据库的操作SQL import java.lang.reflect.Field; public class GenerateSQL { public static void ...
- vue-electron脚手架
vue-electron官方文档(中文):https://simulatedgreg.gitbooks.io/electron-vue/content/cnvue-electron官方文档(英文):h ...
- Nginx(二)------nginx.conf 配置文件
上一篇博客我们将 nginx 安装在 /usr/local/nginx 目录下,其默认的配置文件都放在这个目录的 conf 目录下,而主配置文件 nginx.conf 也在其中,后续对 nginx 的 ...
- macOS下appstore提示未能完成该操作的解决办法
macOS下App Store下载软件,提示:未能完成该操作.(com.apple.commerce.client 错误 500.) 解决办法: 在终端输入 defaults write com.ap ...
- C#.NET 大型通用信息化系统集成快速开发平台 4.1 版本 - 严格的用户账户审核功能
整个集团有几万个用户,一个个用户添加是不现实的,只有每个公司的系统管理员添加.或者用户申请帐户,然后有相应的管理员审核,才会更准确一些. 每个公司.分公司.部门的账户情况只有所在公司的管理员是最清楚的 ...
- Django+nginx+uwsgi部署教程
00-所需工具 xshell:https://www.netsarang.com/zh/downloading/?token=ZlZnVUNsWDJuM0VaZnVPUjZST1dwd0AzYlNte ...
- python线程中的全局变量与局部变量
在python多线程开发中,全局变量是多个线程共享的数据,局部变量是各自线程的,非共享的. 如下几种写法都是可以的: 第一种:将列表当成参数传递给线程 from threading import Th ...
- 论一类每次修改log个结点更新的线段树标记方法
楼房重建(BZOJ2957) 多次询问一个区间中大于区间内这个数之前所有数的数的数量. 每个线段树结点维护该节点的答案c和区间内最大值m.假设有函数get(x,cm)=结点x中答案>cm的长度. ...
- Python类与对象的理解
注意python的类对象与实例对象的区分 类对象与实例对象是相对的,例如:a=1,那么a就是int的一个实例对象,这里的a相对于int来说,a是实例对象,int是类对象.但是int同时又是type的实 ...
