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命令(软件测试第二次作业)的更多相关文章

  1. 逐步实现python版wc命令

    Python 如何处理管道输入输出 sys.stdin 等于打开了一个文件对象,所有输入的文件都会写入到标准输入文件中(键盘) sys.stdout 等于打来了一个文件对象,使用.write()把信息 ...

  2. 使用 python 实现 wc 命令程序的基本功能

    这里使用了 python 的基本代码实现了 Linux 系统下 wc 命令程序的基本功能. #!/usr/bin/env python #encoding: utf-8 # Author: liwei ...

  3. 作业(二)—python实现wc命令

    Gitee地址:https://gitee.com/c1e4r/word-count(为什么老师不让我们用github) 0x00 前言 好久没发博客了,感觉自己的学习是有点偷懒了.这篇博客也是应专业 ...

  4. 软件测试第二周作业 WordCount

    本人github地址:  https://github.com/wenthehandsome23 psp阶段 预估耗时 (分钟) 实际耗时 (分钟) 计划 30 10 估计这个任务需要多少时间 20 ...

  5. 软件测试第二次作业——Fault,Failure,Error辨析与设计测试用例

    Fault 静态错误 ,Failure 外部错误 ,Error 内部错误 问题答案 第一题 1.1 当数组x内的元素≥2时,该循环不会检测到x[0]这个元素. 1.2 test: x=[2, 3, 2 ...

  6. 软件测试第二次作业:初识JUNIT单元测试方法

    软件测试有很多分类,从测试的方法上可分为:黑盒测试.白盒测试.静态测试.动态测试   从软件开发的过程分为:单元测试.集成测试.确认测试.验收.回归等. 在众多的分类中,与开发人员关系最紧密的莫过于单 ...

  7. 2003031121-浦娟-python数据分析第四周作业-第二次作业

    项目 内容 课程班级博客链接 20级数据班(本) 作业链接 Python第四周作业第二次作业 博客名称 2003031121-浦娟-python数据分析第四周作业-matolotlib的应用 要求 每 ...

  8. python学习:简单的wc命令实现

    #!/usr/bin/python   import sys import os   try:     fn = sys.argv[1] except IndexError:     print &q ...

  9. 第二课作业——redis常用命令

    第二课时作业 静哥 by 2016.2.23~2016.2.22   [作业描述] 1.key string list hash结构中,每个至少完成5个命令,包含插入 修改 删除 查询,list 和h ...

随机推荐

  1. 查看linux内存使用情况

    查看内存使用情况 free -m total used free shared buffers cached Mem: -/+ buffers/cache: Swap: used=total-free ...

  2. P2690 接苹果(暴力搜索+记忆化)

    思路: 建树:就是在每一分钟进行分枝,是原地不动,还是移动.然后,走完整个过程. 但是,我其实还是走了弯路,因为,最开始想的是剪枝,没有用记忆化搜索.但是,肯定是能用dp来做,啊啊啊啊阿,能用dp肯定 ...

  3. masm的调试命令(debug)

    -u命令:查看汇编代码: -t命令:执行下一条语句 -g + 的内存:跳转到该内存所对应的语句(再用t命令执行该条命令) -r命令:查看寄存器的内容(后可直接接寄存器的名称,就只查看该寄存器的内容) ...

  4. pytorch visdom可视化工具学习—1—详细使用-2-plotting绘图

    3)plotting绘图 我们已经包装了几种常见的plot类型,以便轻松创建基本的可视化.这些可视化是由Plotly驱动的. Visdom支持下列API.由 Plotly 提供可视化支持. vis.s ...

  5. 【转】打包 压缩 命令tar zip

    https://www.cnblogs.com/centos2017/p/7896807.html tar语法 #压缩tar -czvf ***.tar.gztar -cjvf ***.tar.bz2 ...

  6. Windows10下使用python+selenium实现谷歌浏览器的自动控制

    第一 谷歌浏览器一直是开发人员最喜欢的浏览器,python爬虫在进行抓包时尤其好用,今天为大家带来python+selenium进行自动化控制的安装教程 安装selenium windows下在cmd ...

  7. c++中vector类的用法

    概括:向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container).跟任意其它类型容器一样,它能够存放各种类型的对象.可以简单的认为,向量是一个能够存放任意类型的动态 ...

  8. Spring和SpringMvc详细讲解

    转载自:https://www.cnblogs.com/doudouxiaoye/p/5693399.html 1. 为什么使用Spring ? 1). 方便解耦,简化开发 通过Spring提供的Io ...

  9. LOJ2014 SCOI2016 萌萌哒 并查集、ST表优化连边

    传送门 一个朴素的做法就是暴力连边并查集,可是这是\(O(n^2)\)的.发现每一次连边可以看成两个区间覆盖,这两个区间之间一一对应地连边.可线段树对应的两个节点的size可能不同,这会导致" ...

  10. odoo11登录之后返回的session信息分析

    { "id": null, "jsonrpc": "2.0", "result": { "web_tours& ...