WC项目——python实现,,GitHub地址:h
WC项目——python实现,,GitHub地址:h
GitHub地址:https://github.com/Amy-CC/SCHOOL.git
项目要求:
实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
具体功能(已实现)-c 返回文件字符数-w 返回词的数目-l 返回行数扩展功能(已实现)-s 递归处理目录下符合条件的文件-a 返回更复杂的数据(代码行 / 空行 / 注释行)高级功能(未实现)-x 命令行输入-x参数,程序显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息PSP表格:
解题思路:
看到题目时,我先想到的是实现基础功能。首先考虑的是考虑用sys模块输入,使其能在命令行中输入。然后考虑文件的打开问题,我使用的是py文件所在的文件夹。-c功能考虑了所有的字符,-w功能先使用正则表达式去掉不是字母和空格的字符,然后通过空格分割来计数。-l则很简单,使用len()函数就可以解决。
-w开始的时候想着用os.path模块、os.walk函数,但是发现在这种情况下,我仍然需要对文件列表进行进一步的循环操作,所以放弃了os.walk函数,而是采用递归的方法。
-a我只考虑了c文件,因为c文件无法跟python的源文件同时判断,由于注释#的问题,在c语言中有含义,无法同时读取。首先设置for循环,第一次判断空行的情况,因为标准是少于或等于一个字符,我先把前后的空格去掉,就能判断只有一个字符。第二次判断注释,用另一个函数,用正则判断。第三次判断直接通过len()判断代码行,避免前面两行的影响。
在设计的过程中,我也查找了百度关于python的os库,如何实现命令的输入,文件的读取,还有对于文件目录的函数。我查看了关于python的书籍还有正则表达式的书籍。对于单元测试还是没有完整覆盖,所以最后采用的手动测试的方式。
设计实现过程:
因为首先做好的设计是关于基础功能的,所以在后面实现扩展功能时,发现有着架构的问题,所以在后来需要把读取文件的架构做一些修改:添加一个文件路径列表,在读取文件的时候直接使用readlines()函数,避免在计数的过程中出现文件变量被清空的情况。
在刚开始实现的时候,发现对于命令和文件名的判断函数出现了问题,因为对于正则表达式的不熟悉,所以经过几次的修改才能通过。
在考虑基础功能时,开始写的函数是每个命令每次都重读一次文件,但是这太过于麻烦,需要多个for循环会提高复杂度,所以采用了一次读取,多次使用的readlines()函数。而且readlines()函数会带来问题,就是每个字符串都会出现一个换行符,所以我后来使用了rstrip()来去掉。
扩展功能-s刚开始出现了不知道怎么判断输入的文件后缀名,后来通过分割再拼接的方式,实现读取后缀名。而且因为后缀名有大小写的问题,所以后面加了upper()函数。
扩展功能-a实现的时候因为re模块函数不熟悉,在注释的判断上用了match()函数,发现由于match只针对开头判断,所以后面改变为search函数。
程序结构:
第一个类检查输入的正确性,第二个命令是各种操作的代码。由判断menu()函数来实现对其他操作函数的调用。
代码说明:
一、第一个类:检查正确性
1、判断文件名的正确性
#检查文件的合法性 def check_file(self): if re.match(r"[a-zA-Z0-9]*\..",self.file_name): self.file_list.append(os.path.abspath(self.file_name)) return True elif re.match("([\*\?$]+\.[a-zA-Z]+$)",self.file_name): return True print("The file name is not right.") return False
2、判断命令名字的正确性
#检查命令的合法性 def check_command(self): for i in range(1,self.order_number-1): if (self.order_list[i]!=‘-w‘) and self.order_list[i]!=‘-c‘ and self.order_list[i]!=‘-l‘ and self.order_list[i]!=‘-s‘ and self.order_list[i]!=‘-a‘: print("The order does not exist") return False break return True
二、第二个类:各个操作
1、统计字符数
输入是readlines后的列表,只需统计除了最后换行符的字符串长度
#统计字符数 def re_chara(self,ff): count1 = 0 for i in ff: count1 += len(i.rstrip()) return count1
2、统计单词数
先去掉最后换行符,再通过去掉非空格、非字母的字符,利用空格分割,统计非空字符串,即为单词数。
#统计单词数(由于readlines读出的每一行都会有最后的换行符,所以用rstrip函数去掉) def re_word(self,ff): count2 = 0 string_after = "" for i in ff: string_after+=i.rstrip() new_a = re.sub("[^a-zA-z0-9\s]"," ",string_after) words = new_a.split() for word in words: if word != "": count2 += 1 return count2
3、统计行数
直接返回readlines()后的个数
#统计行数 def re_line(self,ff): return len(ff)
4、递归文件
通过分割拼接,得到完整路径,如果符合后缀名,则放入列表;如果为文件夹则递归寻找。
#查看目录中的文件 def file_recurse(self,pathh): #列出文件夹中所有的文件,包括文件夹 files = os.listdir(pathh) #分割输入的文件名 p1=self.order_list[self.order_number-1].split(".") #拼接为后缀名 p = "."+ p1[1].upper() for f in files: #拼接为完整路径 real_path = os.path.join(pathh,f) #如果是文件 if os.path.isfile(real_path): #分隔路径 spl = os.path.splitext(real_path) if spl[1].upper()==p: #把路径加入文件列表 self.file_list.append(real_path) #若是文件夹,则递归 if os.path.isdir(real_path): self.file_recurse(real_path)
5、统计空行、代码行、注释行
先判断空行,去掉前后的空格等字符,则为显示字符。
注释行需另外函数来统计,因为需要标记/*的特殊情况。
代码行则多于1个代码并且不是注释行。
#判断注释行 def explain_search(self,k,flag): if re.search(r"//",k) != None: flag = 1 return flag if re.search(r"/\*",k)!= None: flag = 0 if re.search(r"\*/",k)!= None: flag=1 return flag
#处理复杂数据 def complex_data(self,ff): #标志空行 empty_l = 0 #标志代码行 code_num = 0 #标志注释行 explanation = 0 #用来/**/注释方式的出现 flag = 0 for i in ff: k = i.strip() if len(k)<=1: empty_l += 1 elif all_operation.explain_search(self,i,flag)==True: explanation += 1 elif len(k)>1: code_num += 1 print("空行:"+str(empty_l)) print("代码行:"+str(code_num)) print("注释行:"+str(explanation))
6、菜单(调用操作、打开文件)
先遍历文件列表,打开文件,再遍历命令列表。
def menu(self): for f in self.file_list: try: with open(f) as file_object: ff = file_object f1 = ff.readlines() print(os.path.abspath(f)) for i in range(1, num-1): if self.order_list[i] == ‘-c‘: print("Characters: "+ str(all_operation.re_chara(self,f1))) elif self.order_list[i] == ‘-w‘: print("Words: "+ str(all_operation.re_word(self,f1))) elif self.order_list[i] == "-l": print("Lines: "+ str(all_operation.re_line(self,f1))) elif self.order_list[i] == ‘-s‘: continue elif self.order_list[i] == ‘-a‘: all_operation.complex_data(self,f1) else: print("the order is wrong, please try again") except FileNotFoundError: msg = "file: " + os.path.basename(f) + " does not exist" print (msg)
三、相当于main函数的主体
先判断文件列表是否为空,选择是否进入递归文件函数。
还要查询递归后,是否有此类文件,有才执行菜单操作,否则退出。
num = len(sys.argv)#设置一个文件夹列表,存放文件路径file_list=[]#检查是否有足够的参数if num<3: print("The parameters are not enough.")else: instance = check_every(sys.argv[num-1],file_list, num, sys.argv) if instance.check_file()==True and instance.check_command()==True: instan = all_operation(file_list, num, sys.argv) if len(file_list)==0: pp = os.path.abspath(__file__) instan.file_recurse(os.path.dirname(pp)) if len(file_list)==0: print("There is no such file") else: instan.menu()
测试结果:
各种命令的测试(正常源文件):
目标文件:
若是命令不符合:
若是文件名不符合:
-w操作的实现
目标目录:
空白文档:
单个字符文档(字母):
单个字符文档(非字母,而是控制字符):
项目总结:
1、因为是第一次使用python开发一个项目,所以很多东西都需要查询资料,例如各种的模块,由于各种模块运用的还不是很熟练,而导致了一些混淆的现象,例如re模块的match和search函数。
2、而且在过程中遇到了很多的困难,只有靠不断的测试,查百度才了解了自己的错误原因,进行一遍遍的修正。了解到了查询资料、测试的重要性。
3、尽管实现了基础功能和扩展功能,项目的时间复杂度还是比较高的,还需要进一步的优化。
4、学会了使用GitHub来控制自己的版本操作,尽管上传失败了很多次,在一次次的摸索中终于上传成功了。
5、明白了开始的规划其实很重要,由于我之前规划的不好,导致后来增加功能需要重新架构,浪费了很多的时间。
6、之前没有学会写一个函数及时测试, 导致后面的函数增加后,测试难度加大,需要用更多的时间来排查错误,下次需要改进。
WC项目——python实现
评论关闭