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实现

评论关闭