• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
笼中鸟
stay hungry stay foolish
博客园    首页    新随笔    联系   管理    订阅  订阅
20200917-2 词频统计

此作业的要求参见:https://edu.cnblogs.com/campus/nenu/2020Fall/homework/11206

该作业采用的编程语言为Python,其代码的地址为:https://e.coding.net/xucancan1/words_count/words_count.git

我用的是git和tortoisgit将文件传入到coding.net上的

功能1 小文件输入

功能2 支持命令行输入英文作品的文件名

功能3 支持命令行输入存储有英文作品文件的目录名

功能4 从控制台读入英文单篇作品

 

一. 功能1

重难点:

(1)对于第一个题,如何把文件名输入到命令行参数上,让命令行的参数来进行处理问题?注意这里要与input()函数的输入区分开来

如果我们使用input()函数获得输入数据而进行处理,这种在命令行上运行时,会要求敲两次回车键才能执行,第一行输入的数据都输入到了sys.argv中,而第二行输入的数据,才是被输入到了input()了,然后通过input()来处理,虽然结果相同,但显然不符合题意。

正确做法:引入sys包,通过sys,argv数组来接收输入的文件名以及其他参数 (ps:不是用input来接受文件名进行处理)

(2)如何通过命令行输入的文件名来读取对应的文件?

通过使用sys.argv[0],sys.argv[1],sys.argv[2],来进行操作,一般sys.argv[0]指的是pthon文件名的路径,sys.argv[1],sys.argv[2],才是接下来的要处理的参数

对于打开文件用open()函数:f=open("sys.argv[0]",''r",encoding="UTF-8"),这里有一个很容易错的问题,那就是这个需要加一个utf-8,当时在这个问题卡了很久,不明白问题所在,通过百度才明白。

由于.py文件默认ASCII编码,中文在显示时会做一个ASCII到系统默认编码的转换,这时会报错,所以在open()函数里加‘encoding='utf-8’,意为以字节为单位对Unicode进行编码保存,这样遇到中文也不会出错。

(3)如何将文件的内容分割成单词,然后存储到列表中?

我用的是split()函数按照一定的规则进行分割;首先将文件统一转换成小写(大写也行)进行预处理,避免重复计算,然后用list()函数将每个单词列表化和filter()函数消除列表中的空格

(4)如何统计总单词数和统计各个单词出现的次数?

对于这个问题,我本来想着用一个for循环来遍历列表。将每个单词出现的个数用字典来统计的,但是后来想了一下这个有点浪费体力,然后刚好有一个collection包里有一个counter()函数,直接统计列表中的单词,返回的结果是一个元组的列表,刚好符合我要的功能。接着就是遍历这个字典统计各个单词的出现的个数以及进行打印。

(5)如何统计单词数出现最多的前n个单词的列表?

这里其实可以使用sort函数对字典的键值进行排序,然后选择只打印前10个单词以及出现的个数,但是这样多了一个for循环判断,我果断放弃了,又选择一个一个内置函数most_common(10),这个函数好处作用是统计了前10的单词,返回是一个字典,然后只需打印遍历这个字典就行。

重要代码:

#功能1:小文件输入。
def count_words(filename):
    #print("c")
    s=".txt"
    if s in filename:
        path=filename
    else:    
        path=filename+s

    try:
        with open(path,"r",encoding="UTF-8") as f_obj:
             content = f_obj.read().lower()
    except FileNotFoundError:
        msg = "sorry,the file " + filename + " does not exist."
        print(msg)
    else:
    
        #为避免重复计算大小写单词,所以将所有单词全部转换为小写
        lists= re.split(r'[",", ".", "!", "?", ";", ""","--","\n"]', content)     #通过正则表达式和split()函数生成文件内容的列表 
        lists= list(filter(None, lists))     #消除列表中的空格
        words=Counter(lists)
        #遍历字典,统计键值对数
        num=0
        for key,value in words.items():
            num+=1
        #功能1不输出words,功能2输出words
        if sys.argv[1]=="-s":
            print("total"+" "+str(num))
        else:
           print("total"+" "+str(num)+" words")
        #most_common(n)返回统计单词数出现最多的前n个单词列表
        maxwords=words.most_common(10)
        for i in maxwords:
           print("%-10s%5d"%(i[0],i[1]))

 执行效果截图:

 

二. 功能2

重难点:

(1)如何支持直接输入文件名,而不需要加后缀名.txt和-s就可以统计文件的各个单词次数呢?

对于这个问题,要与第一题区分,第一题是加-s就运行执行这个文件,我们只要判断一下sys,argv[1]是否是-s,如果有,就执行第一个功能,如果没有就执行第二个功能,接着然后在输入的文件后面添加一个.txt后缀。

(2)如何处理这个文件的大小写单词和非字母对单词词统计的影响?

对于这两个问题,当统计次数时,首先解决大小写的影响,我用的是.lower()函数将全部单词转化为小写,(这里也可以用.upper()函数转换成大写),这样做可以避免重复计算单词。其次是解决非字母的字符,我使用的是findall(r"[a-z0-9^-]+",f.read().lower())函数来处理的,它的作用是通过正则表达式和findall()函数排除不符合的字符,从而生成文件内容的列表,其实我后来想了一下,其实也可以用replace()函数把那些不符合单词的字符替换掉。

(3)有关功能1和功能2的区别?

功能1和功能2除了上述的输入有区别外,还有输出的区别:功能2是要输出“words”这个单词的,功能1不输出该单词。

重要代码:

#功能2:支持命令行输入英文作品的文件名
def count_words02(name):
    
    # 判断传入的命令行参数后缀部分是否含有.txt
    d=".txt"
    if d in name:
        path=name
    else:     #若没有,要加上,再作为打开路径
        path=name+d
   
    f=open(path,"r",encoding="utf-8")     
    lists=findall(r"[a-z0-9^-]+",f.read().lower())#通过正则表达式和findall()函数生成文件内容的列表
    words=Counter(lists)
    #遍历字典,统计键值对数
    num=0
    for key,value in words.items():
        num+=1
    #功能1不输出words,功能2输出words
    if sys.argv[1]=="-s":
        print("total"+" "+str(num))
    else:
        print("total"+" "+str(num)+" words")
    #most_common(n)返回计数值最大的n个元素的元素列表
    maxwords=words.most_common(10)
    for i in maxwords:
        print("%-10s%5d"%(i[0],i[1]))

 执行效果截图:

 

三.功能3

重难点:

(1)如何判断你输入的参数是文件还是文件夹?

对于这个问题,我百度了一下,首先需要os这个包,这个包里面带了一个os.path.isdir()函数,它的作用是用于判断某一对象(需提供绝对路径)是否为目录,这个要与os.path.isfile()函数区分开来他的作用是判断对象是否为一个文件,具体用法区别详见:https://blog.csdn.net/m0_37443131/article/details/81231763

(2)如何获取当前.txt文件的绝对路径?

这个问题困扰了很久,我当时想取指定文件或目录的绝对路径(完整路径),想起OS模块不是有个取文件绝对路径的方法os.path.abspath(),结果出现了很大的问题。

例如:获取文件‘test.py’的完整路径

test.py的完整路径是:D:\python_t\IO目录处理\test.py,而不是D:\python_t\test.py,看到这个结果我就泪奔了,不是取绝对路径么?怎么不是真实的完整路径?上级目录不见了?很多疑惑?

其实通过这个例子,我们可以看出os.path.abspath()函数无法获取指定文件的绝对路径,而是需要加文件路径os.path.abspath(path),所以我为了获得绝对路径,分为了两个部分,第一部分是获得该文件夹的上级目录 domain = os.path.abspath("."), domain获取的是当前文件的上一级目录文件夹,然后加上当前文件夹;第二部分是获得需要访问的.txt文件,两个部分加起来,就是.txt文件的绝对路径。

详细用法:https://blog.csdn.net/funnypython/article/details/78733115

(3)在获得文件夹路径时,需要把所有的\\更改为/

因为python返回路径时目录级别都是\\这个形态的,需要将他转换成 / 这个形态,用了replace()函数替换

(4)如何统计文件夹下的.txt文件的单词出现次数?

由于获得了该文件夹的绝对路径,再加上需要访问的.txt文件,两部分加起来就是该.txt文件的绝对路径,然后用open()函数,对这个文件进行读取,去除一些不符合单词的字符,然后用counter()函数统计单词出现的个数。

重要代码:

 #功能3
    elif os.path.isdir(sys.argv[1]):
       #print(sys.argv[1])
        filecount(sys.argv[1])
#功能3:传入文件夹,批量统计文件夹下各文件的单词的个数
def filecount(filename):
    files=os.listdir(filename)#将文件夹下的文件列表化
   # print(files)   #输出该文件下所有的文件
    print("\n")
    domain = os.path.abspath(".") #获取文件的上一级目录文件夹,输出的是D:/homework01/
    domain = domain.replace('\\', '/') + '/' + filename + '/'   #将所有的\\变成“/”,且加上当前文件,返回相当于D:/homework01/book/
    
    for file in files:  #遍历文件下的所有文件
        
        path=domain+file   #这才是该文件的真实路径,path=D:/homework01/book/file.txt
        
        (filename1, extension) = os.path.splitext(file)#将文件名与后缀分开,将文件名单列出来
        print(filename1)   
        f=open(path,"r",encoding="utf-8")     
        lists=findall(r"[a-z0-9^-]+",f.read().lower())#通过正则表达式和findall()函数生成文件内容的列表
        words=Counter(lists)  #Counter函数是用来跟踪值出现的次数,以字典的键值对形式存储,其中元素为key,其计数为value  
        #遍历字典,统计键值对数
        num=0
        for key,value in words.items():
           num+=1
        print("total"+" "+str(num)+" words")
           
        #most_common(n)返回计数值最大的n个元素的元素列表
        maxwords=words.most_common(10)
        for i in maxwords:
           print("%-10s%5d"%(i[0],i[1]))
        print('------------------')
        

执行效果截图:

 

四.功能4

重点难点:

(1)如何对功能4的理解?

其实开始看这个功能4时,我还蒙,但是看了一下测试用例,我慢慢明白了,首先确定 the_show_of_the_ring是一个文件,其次哪个>wf -s < the_show_of_the_ring,这个<符号是一个重定向符,-s是添加的区别于其他功能的标志,所以题意是统计重定向的文件的单词个数。

(2)如何在cmd命令台接收一大段英文文章?

首先确定sys.argv数组是不能接受这么大段英文文章的,所以我引用了input()函数,sys.argv用来存储这个.py文件路径,我先判断一下输入,sys.argv数组的里面内容的个数为1,就表明它接下来要输入一段英文文章;接着我用input()函数来接收这段英文。然后通过input()函数接收的英文进行分割处理成单词列表,接下来就是统计单词个数了

重要代码:

#功能4:输入一篇文章统计单词字数
    if(len(argv)==1):
        str = input()
        str = re.sub('[^a-zA-Z]',' ',str)  #sub()函数将不为字母的替换空格
        count_words03(str)

(3)如何统计重定向文件的单词,即< the_show_of_the_ring?

对于这个问题,我完全没有头绪,后来通过伟大的百度,查阅了一下windows下文件重定向的相关问题,大概总算明白了是什么回事。然后看到了一个sys.stdin.readline() 函数,这个函数功能是存储重定向的文件名接着去读这个文件的内容,返回这个文件的内容。刚好返回的内容是我们需要的,我们可以通过相关处理变成单词列表,继续统计该列表的单词。

sys.stdin.readline()函数用法详见:https://www.jb51.net/article/169914.htm

(4)如何判别wf.py -s是执行功能4的重定向呢?

这个功能区别于功能1,功能1中的sys.argv数组里面的len(sys.argv)是3,而功能4中的len(sys.argv)数组里面的是2,接下来用sys.stdin.readline() 函数接收重定向的文件,并返回该文件的内容。

重要代码:

#功能1和功能4
    elif sys.argv[1]=='-s':
        if(len(argv) == 3):   #执行功能1
            #print(sys.argv[2])
            count_words(sys.argv[2])
               
        elif(len(argv)==2):   #执行功能4:重新定向文件
           redirect_words = sys.stdin.readline()  # 存储重定向的文件名接着去读这个文件的内容,返回这个文件

重要代码:

#功能4:重定向
def count_words03(string):
    print("\n")
    lists = string.replace('\n', ' ').lower().split()  # 用空格去掉所有的\n,且转换成小写,进行分割单词
    words=Counter(lists)  #Counter函数是用来跟踪值出现的次数,以字典的键值对形式存储,其中元素为key,其计数为value  
    #遍历字典,统计键值对数
    num=0
    for key,value in words.items():
        num+=1
    print("total"+" "+str(num)+" words")
           
    #most_common(n)返回计数值最大的n个元素的元素列表
    maxwords=words.most_common(10)
    for i in maxwords:
        print("%-10s%5d"%(i[0],i[1]))
     
        

#定义main函数
def main(argv):
    #功能4:输入一篇文章统计单词字数
    if(len(argv)==1):
        str = input()
        str = re.sub('[^a-zA-Z]',' ',str)  #sub()函数将不为字母的替换空格
        count_words03(str)
        
    #功能1和功能4
    elif sys.argv[1]=='-s':
        if(len(argv) == 3):   #执行功能1
            #print(sys.argv[2])
            count_words(sys.argv[2])
               
        elif(len(argv)==2):   #执行功能4:重新定向文件
           redirect_words = sys.stdin.readline()  # 存储重定向的文件名接着去读这个文件的内容,返回这个文件的内容
           
           redirect_words= re.sub('[^a-zA-Z]',' ',redirect_words)
           count_words03(redirect_words)
                 

执行效果图:

 

五 功能5

 在文件中统计给定的单词长度为m的次数前n个的单词

重难点:

(1)如何解析题目的需求?

我为了把这个功能模块整合到原先设计好的程序框架中,特定对功能5做了标记,这是为了与其他功能的执行区分,用了-h这个标记来执行功能5,剩下的就是功能5的需求进行设计。

(2)如何将两个变量参数输入进去,并且让它们执行相应的功能?

对于这个问题我是用input()函数来接收它们,由于input()函数返回的是字符串类型,所以我用了int()函数将他们强制转换成整形,然后把它们输入到功能5模块中进行处理,接着就是读取文件,将文件内容按照正则表达式分割成单词列表。

#功能5: 在文件中统计给定的单词长度为m的次数前n个的单词
    if(sys.argv[1]=="-h"):
        str1=input()
        a=int((str1.strip()))
        str2=input()
        b=int((str2.strip())))
        count_words04(sys.argv[2],a,b)
        

(3)注意input()函数,这个函数它也接收空格,回车换行等字符,在将它强制转换时,需要用striip()将这些过滤掉,然后才能用int()函数转成整数

(4)如何获得文件中单词长度为m的单词次数前n的单词列表呢?

首先,解决这个问题分为两步,第一步在文件中获取所有单词长度为m的单词;我先用counter()函数统计各个单词的出现的次数,由于counter()函数返回的是单词元组的列表,在这个问题,我开始以为counter()函数返回的是字典,后面造成了一定隐形错误,然后我遍历这个列表,找出所有单词长度为m的单词,接着将它们逐个添加到一个空字典中;第二步在第一步的基础上,统计单词次数前n的单词;由于获得了所有单词长度为m的单词的字典,接着对这些字典的键值进行排序,我本来是想用一个冒泡排序进行排序的,后来想着这样太麻烦了,所以直接用most_common()函数统计出前n的单词 以及次数。

 words=Counter(lists)
        #遍历元组,统计键值对数
        num=0
        words_dic = {}
        for key,value in words.items():
            d={}
            if(len(key)==lent): #统计长度为lent的单词
                d[key]=value
                words_dic.update(d)
                
            num+=1
        print("\n")
        print("total"+" "+str(num)+" words")
        print("你选择了长度为"+str(lent)+"的单词")
        print("统计单词数出现最多的前"+str(n)+"个单词的列表")
        words_list=Counter(words_dic)

 (5)关于most_common()函数的错误用法的血泪史。

错误1:调用most_common()函数有限制,我一直以为可以通过字典来调用它,然后我一直运行,不停的报错,(ps:我英语差,当时看不懂错误提示)然后我将错误提示提交给百度,搜查相关的问题,然后知道了问题关键所在,原来是most_commom()函数的用法出现问题,接着我查了一下most_commom()函数的用法,字典不能调用它,列表可以调用它。然后我就再次使用counter()函数,将字典转成带有元组的列表,这样就可以使用most_commom()函数了。

错误2:most_common()函数只返回前n个单词,它不管第n+1个单词是否与第n个单词相等,这样造成的后果会丢掉那些原本第n+1个单词和第n个单词相等的单词。

错误运行结果截图:

从上图可以看出,长度为4的单词排序,取单词数出现前3的单词,第4,第5,第6的单词个数与第3的单词个数相等,但是它们被舍弃了,没有被显示,这种返回结果非常不好。

针对上面的出现的问题,我重新定义了一个get_count(dct, n)函数来解决,当相等的情况下,相等的需要全部被显示出来。

#返回统计单词数出现最多的前n个单词列表
def get_count(dct, n):
  data = dct.most_common()
  val = data[n-1][1] 
  return list(takewhile(lambda x: x[1] >= val, data))

正确运行结果截图:

 

重要代码:

def main(argv):
    #功能5: 在文件中统计给定的单词长度为m的次数前n个的单词
    if(sys.argv[1]=="-h"):
        str1=input()
        a=int((str1.strip()))
        str2=input()
        b=int((str2.strip()))
        count_words04(sys.argv[2],a,b)
        
#功能5:查找特定长度单词的个数
def count_words04(filename,lent,n):
    s=".txt"
    if s in filename:
        path=filename
    else:    
        path=filename+s

    try:
        with open(path,"r",encoding="UTF-8") as f_obj:
             content = f_obj.read().lower()
    except FileNotFoundError:
        msg = "sorry,the file " + filename + " does not exist."
        print(msg)
    else:
    
        #为避免重复计算大小写单词,所以将所有单词全部转换为小写
        lists= re.split(r'[",", ".", "!", "?", ";", ""","--","\n"]', content)     #通过正则表达式和split()函数生成文件内容的列表 
        lists= list(filter(None, lists))     #消除列表中的空格
        words=Counter(lists)
        #遍历元组,统计键值对数
        num=0
        words_dic = {}
        for key,value in words.items():
            d={}
            if(len(key)==lent): #统计长度为lent的单词
                d[key]=value
                words_dic.update(d)
                
            num+=1
      
        print("total"+" "+str(num)+" words")
        print("你选择了长度为"+str(lent)+"的单词")
        print("统计单词数出现最多的前"+str(n)+"个单词的列表")
        print(words_dic)
        words_list=Counter(words_dic)
        # 本来想用most_common(n)返回统计单词数出现最多的前n个单词列表,后来当有多个单词相等的时候,这个函数只取前n个,其实第n+1个也是跟第n值相等,但是这个函数不能实现
        maxwords=get_count(words_list,n)
        #maxwords=words_list.most_common(n)
        print("\n")
        print("统计结果为:")
        for i in maxwords:
           print("%-10s%5d"%(i[0],i[1]))
    

执行结果截图:

 

6.psp

功能 预计花费时间 实际花费时间 时间差 原因
功能1 80min 123min 43min 对python命令台输入输出不太熟悉
功能2 60min 105min 45min 对参数输入没把握好,且处理后缀名花了一段时间
功能3 90min 146min 76min 对于获取绝对路径不理解,花了很久解决这个问题
功能4 100min 152min 52min 首先对题目的解析花了一段时间,且设计相关功能也花了很多时间
功能5 40min 60min 20min 功能测试,以及功能设计需求超出了时间
测试    20min     123min  103min 对于python文件需要转换成.exe文件,安装pyinstaller严重超出了时间

 

 

 

 

 

 

 

 

 

 

 

 

7.总结

完成这个部分作业,我大概分为了三个阶段:

第一阶段熟悉安装使用git和totorisgit,熟练将文件从coding.net或github中pull下来或者push到云端。

第二阶段1-5功能代码设计的实现

第三阶段将.py文件转换成.exe文件                     

对于第一阶段,说句实话,花了很久时间才把totorisegit这个工具学会(当然也可以用git命令行),用它可以直接push&pull代码,而且可以将代码push到云端存储着,真的很方便,虽然在学习使用工具这个过程中摸索地很痛苦,但是学会了后,我感觉totorisgit工具和云端仓库真的太nice了!

对于第二个阶段功能代码设计,python很多方法的实现完全可以调用相关函数来执行,我在这个问题上由于python知识储备不是很足,所以花了很多时间研究其中的函数,最终才将相关的函数运用到实际操作中。

对于第三阶段下载安装pyinstaller,将.py文件转换成.exe文件,这个我也摸索了很久,主要是在.exe文件的查询上,一开始不知道它转化到哪里了,后来查询百度,发现它存到了dist文件中

通过这次的编程实践,让我学习到了很多以前没有学到的知识,同时也让我知道了自己的不足,我还需要不停地努力!

 

关于totoisgit和git安装是相关问题:

1.Git将本地代码推到远程仓库的步骤:[https://blog.csdn.net/weixin_39910711/article/details/89955544]

2.利用totorisgit在github上下载代码:https://blog.csdn.net/zdp072/article/details/51966586

3.远程仓库连接错误时,如何清除连接[https://www.cnblogs.com/wollow/p/10840016.html]

4.认证失败时,怎么办[https://www.jianshu.com/p/c095300d569e]

5.使用Git客户端下载代码的流程[https://blog.csdn.net/wyqwilliam/article/details/82881609]

6.Git clone和Git pull的简要区别[https://www.jianshu.com/p/c6a0397ec6f5]

7.如何将.py文件转化为可执行的.exe文件:https://blog.csdn.net/qq_36604847/article/details/81509113 

 

posted on 2020-09-21 18:24  xucancan  阅读(519)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3