软工作业5:词频统计——增强功能
一、基本信息
1.1 编译环境、项目名称、作者
1 #编译环境:python3,Geany 2 #项目名称:结对项目词之词频统计—增强功能 3 #作者:1613072037 张铭锐 4 # 1613072036 谭琪
1.2项目地址
- 本次作业地址: https://edu.cnblogs.com/campus/ntu/Embedded_Application/homework/2088
- 项目git地址: https://gitee.com/ntucs/PairProg/tree/SE036_037
二、项目分析
-
程序运行模块(方法、函数)介绍
Task 1. 接口封装 —— 将基本功能封装成(类或独立模块)
将基本功能:统计文本总词数,统计文本最多的十个单词及其词频这两个方法封装在类wordclass中
1 #coding=gbk 2 import re 3 4 class wordclass: 5 6 def process_file(dst): # 读取文件 7 countline = len(open(dst, 'r').readlines()) # 文本的行数countline 8 with open(dst) as f: 9 bvffer = f.read() 10 f.close() 11 return bvffer 12 13 def process_buffer(bvffer): 14 if bvffer: 15 for ch in '“‘!;:,.?”': 16 bvffer = bvffer.lower().replace(ch, " ") 17 wordmatch = "^[a-z]{4}(\w)*" #正则表达式至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写 18 # 将文本内容都改为小写且去除文本中的中英文标点符号 19 words = [] 20 for i in range(len(bvffer)): 21 word = re.match(wordmatch, bvffer[i]) # 匹配list中的元素 22 if word: # 匹配成功,加入words 23 words.append(word.group()) 24 # strip()删除空白符(包括'/n', '/r','/t');split()以空格分割字符串 25 words = bvffer.strip().split() 26 word_freq = {} 27 for word in words: # 对words进行统计 28 word_freq[word] = word_freq.get(word, 0) + 1 29 return word_freq, len(words) 30 31 def output_result(word_freq): 32 if word_freq: 33 sorted_word_freq = sorted(word_freq.items(), key=lambda v: v[1], reverse=True) 34 for item in sorted_word_freq[:10]: # 输出 Top 10 的单词 35 print("单词:%s 频数:%d " % (item[0], item[1])) 36 return sorted_word_freq[:10] 37 38 def result(dst): 39 buffer = wordclass.process_file(dst) 40 word_freq, countwords = wordclass.process_buffer(buffer) 41 print('文本总单词数:' + str(countwords)) 42 print('文本中最多的10个单词及其词频') 43 wordclass.output_result(word_freq)
编写一个test.py,通过import argparse模块,可以在cmd命令行中测试上述的封装类
1 #coding=gbk 2 import WordCount 3 import argparse 4 if __name__ == '__main__': 5 parser = argparse.ArgumentParser(description="your script description") # description参数可以用于插入描述脚本用途的信息,可以为空 6 parser.add_argument('--file', '-file', type=str, default='D:/桌面/python_work/SE037/Gone_with_the_wind.txt', help="读取文件路径")#default属性是默认值,当参数输入错误时使用默认参数 type后表示参数的类型 7 args = parser.parse_args() # 将变量以标签-值的字典形式存入args字典 8 path = args.file #通过键值对形式取值 9 WordCount.wordclass.result(path) #此处为类的调用
下图为统计所用的文本Gone_with_the_wind_txt
下图为在IDE环境下运行截图
下图为在命令行传参截图
Task 2. 增加新功能
- 词组统计:能统计文件夹中指定长度的词组的词频
- 自定义输出:能输出用户指定的前n多的单词与其数量
封装类wordclass的代码:
1 #coding=gbk 2 import re 3 class wordclass: 4 def __init__(self,path,m,n,o): #类wordclass的构造方法 path为文件路径 m为词组长度 n为输出单词数量 o为生成的result文件存储路径 5 self.path=path 6 self.m=m 7 self.n=n 8 self.o=o 9 10 def process_file(self): 11 countline = len(open(self.path, 'r').readlines()) # countline为文本行数 12 with open(self.path) as f: 13 bvffer = f.read() 14 f.close() 15 return bvffer, countline 16 17 18 def process_buffer(self,bvffer): # 处理缓冲区,返回存放每个单词频率的字典word_freq 19 if bvffer: 20 word_freq = {} 21 words = [] 22 for ch in '“‘!;:,.?”': 23 bvffer = bvffer.lower().replace(ch, " ") 24 words = bvffer.strip().split() 25 match='[a-z]+' 26 for i in range((self.m)-1): 27 match+='\s[a-z]+' #m长度词组的正则表达式 28 result = re.findall(match, bvffer) # 正则查找词组 29 for word in result: 30 word_freq[word]=word_freq.get(word,0)+1 31 return word_freq,len(words) 32 33 34 35 def output_result(self,word_freq): 36 if word_freq: 37 sorted_word_freq = sorted(word_freq.items(), key=lambda v: v[1], reverse=True) 38 for item in sorted_word_freq[:self.n]: # 输出 Top n 的单词 39 print("单词:%s 频数:%d " % (item[0], item[1])) 40 return sorted_word_freq[:self.n] 41 42 def result(self): 43 print('需查询的文本路径:'+str(self.path)) 44 print('需查询的词组长度:'+str(self.m)) 45 print('需查询的词频Top:'+str(self.n)) 46 buffer,countline=wordclass.process_file(self) 47 word_freq,lenwords=wordclass.process_buffer(self,buffer) 48 clines= 'lines:' + str(countline) 49 cwords = 'words:' + str(lenwords) 50 print(clines) 51 print(cwords) 52 items =wordclass.output_result(self,word_freq) 53 with open(self.o, 'w') as w: 54 w.write(clines+'\n') 55 w.write(cwords+'\n') 56 for item in items: # 格式化 57 item = '<' + str(item[0]) + '>:' + str(item[1]) + '\n' 58 w.write(item) 59 print('将该文件写入路径为:'+self.o) 60 w.close() 61 62 63 64 65 66 #if __name__ == '__main__': 67 # obj = wordclass('D:/桌面/python_work/Gone_with_the_wind.txt', 2, 3, 'D:/桌面/python_work/SE037/result.txt') 68 # obj.result() 69 70 71 72 73
import argparse模块进行命令行传参测试代码:
1 #coding=gbk 2 import wordclass 3 import argparse 4 import cProfile 5 import pstats 6 7 def main(): 8 parser = argparse.ArgumentParser(description="your script description") # 创建一个解析对象 description参数可以用于插入描述脚本用途的信息,可以为空 9 parser.add_argument('--i', '-i', type=str,required=True,help="读取文件路径")#添加--i标签,标签别名可以为-i,required=Truerequired表示---参数是必需的,并且类型为str,输入别的类型会报错。 10 parser.add_argument('--m', '-m', type=int,required=True,help="输出的单词数量") 11 parser.add_argument('--n', '-n', type=int,required=True,help="输出的单词数量") 12 parser.add_argument('--o', '-o', type=str,required=True,help="读取文件路径") 13 args = parser.parse_args() # 进行解析 将变量以标签-值的字典形式存入args字典 14 path = args.i 15 m = args.m 16 n = args.n 17 o = args.o 18 obj = WordCount.wordclass(path, m, n, o) 19 obj.result() 20 if __name__ == '__main__': 21 cProfile.run("main()", "pstats_result") 22 # 把分析结果保存到文件中,不过内容可读性差...需要调用pstats模块分析结果 23 p = pstats.Stats("pstats_result") # 创建Stats对象 24 p.strip_dirs().sort_stats("call").print_stats() # 按照调用的次数排序 25 p.strip_dirs().sort_stats("cumulative").print_stats() # 按执行时间次数排序
运行成果图:
三、性能分析
本次实验在作业4基础上进行,在时间、空间复杂度方面差不多,所以运行很流畅。
1.根据调用次数分析
2.根据执行时间分析
性能图表:
四、PSP 表格
五、事后分析与总结
1.简述结对编程时,针对某个问题的讨论决策过程
我们在网上查找了python命令行传参的方式,了解到传递参数有三种方法:1、参数通过sys.argv传递,它的类型是一个list类型,其中的元素为字符串。2、通过getopt模块解析Python传入的参数,它能解析带'-'和'--'格式的参数。它的函数原型为:getopt.getopt(args, options[, long_options])。3、使用argparse模块解析命令行参数。通过我和谭琪的讨论,我们选择了argparse模块来传参,因为argparse是一个可以自动生成帮助信息和错误信息的模块,有利于命令行传参的调试分析。
2.评价对方:请评价一下你的合作伙伴,又哪些具体的优点和需要改进的地方。 这个部分两人都要提供自己的看法。
谭琪评价张铭锐:张铭锐同学在编程方面能力很强,在python语言方面很熟练,也很有自己的编程想法。需要改进的地方:在编码过程中需要能够灵活变通。
张铭锐评价谭琪:谭同学做项目态度很认真,学习能力也很强,我们一开始对python命令行传参不是很了解,他通过学习csdn中的相关帖子教程后掌握了这些方法。需要改进的地方:虽然python基础还不错,但还需要学习python深层次的知识。
3.评价整个过程:关于结对过程的建议
结对编程需要双方不断提出自己的想法并加以结合,耐心理解对方的思路。每人负责相应的模块,但也要融会对方的进程,相互补充和学习。在解决问题的过程中,我们相互启发和纠正,对于有争议的问题也是通过实践来印证。通过此次的结对编程,我们体会到合作的重要性以及不理解对方时的艰难,使我们对合作开发有了一定的理解和基础。
4.结对编程照片
5.其他
经过这两次的合作编程,我们双方的编程能力都有提高。在合作的过程中,我们互相学习、帮助,一起解决问题,这两次的合作对我们非常有意义。