软件工程作业——Word Counter

该项目模仿wc.exe工具,提供字符、单词和行数统计,扩展功能包括代码行、空行和注释行计数,支持图形界面操作。

github地址

https://github.com/Pryriat/Word_Counter

项目说明

  • wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。

  • 实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。

  • 具体功能要求:
    程序处理用户需求的模式为:

    wc.exe [parameter][file_name]

  • 基本功能列表:
    wc.exe -c file.c //返回文件 file.c 的字符数

    wc.exe -w file.c //返回文件 file.c 的词的数目

    wc.exe -l file.c //返回文件 file.c 的行数

  • 扩展功能:
    -s 递归处理目录下符合条件的文件。
    -a 返回更复杂的数据(代码行 / 空行 / 注释行)。

空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。

代码行:本行包括多于一个字符的代码。

注释行:本行不是代码行,并且本行包括注释。一个有趣的例子是有些程序员会在单字符后面加注释:

} //注释

在这种情况下,这一行属于注释行。

[filename]:文件或目录名,可以处理一般通配符。

  • 高级功能:

-x 参数。这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。

  • 需求举例:
      wc.exe -s -a *.c
    返回当前目录及子目录中所有*.c文件的代码行数、空行数、注释行数。

PSP表格

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划1020
Estimate估计这个任务需要多少时间1020
Development开发300340
Analysis需求分析 (包括学习新技术)3060
Design Spec生成设计文档1010
Design Review设计复审 (和同事审核设计文档)00
Coding Standard代码规范 (为目前的开发制定合适的规范)00
Design具体设计2020
Coding具体编码100110
Code Review代码复审4040
Test测试(自我测试,修改代码,提交修改)100100
Reporting报告5080
Test Report测试报告1010
Size Measurement计算工作量1010
Postmortem & Process Improvement Plan事后总结, 并提出过程改进计划3060
合计360440

解题思路描述

1482419-20180914101904672-2140560446.jpg

设计实现过程

1482419-20180914101942753-796518418.jpg

代码说明

控制台输入输出

#coding = UTF-8
from wc_class import word_counter
from ui_class import wc_ui
import argparse
import sys
from PyQt5.QtWidgets import *
from PyQt5.Qt import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

opt = {}
if len(sys.argv) == 1:
    sys.argv.append('-h')# 如果未输入参数,则自动添加执行-h
parser = argparse.ArgumentParser(description='Word_Counter by Chenobyl',add_help=True)
parser.add_argument('-c',help='character counts',action="store_true")
parser.add_argument('-w',help='word counts',action="store_true")
parser.add_argument('-l',help='line counts',action="store_true")
parser.add_argument('-a',help='extra informations',action="store_true")
parser.add_argument('-s',help='match  files',action="store_true")
parser.add_argument('-x',help='',action="store_true")
parser.add_argument('-f',default=None,help='input file')
args = parser.parse_args()#参数提取
for x,y in args._get_kwargs():
    opt[x]=y
a = word_counter(opt)
if opt['x']:
    app = QApplication(sys.argv)
    u = wc_ui()
    u.show()
    sys.exit(app.exec())
elif opt['s']:
    a.file_select()
else:
    a.file_process(opt['f'])

文件统计类

#coding = UTF-8
import re
import codecs
import getopt
import sys
import glob

class word_counter:
    def __init__(self,opt):#用于存储文件统计结果的属性
        self.char_num = 0
        self.word_num = 0
        self.lines = 0
        self.nullline = 0
        self.single_comment = 0
        self.multi_comment = 0
        self.code_line = 0
        self.is_null = False
        self.file = ''
        self.opt=opt

    def isnull(self,string):
        '''
        判断文件是否为空
        '''
        null_pattern = re.compile('.|\s')#判断文件是否有字符
        tmp_result = null_pattern.findall(string)
        if len(tmp_result) == 0:
            return True
        return False

    def char_count(self,string):
        '''
        文件字符数统计
        '''
        return len(string)

    def word_count(self,string):
        '''
        文件单词数统计
        '''
        word_pattern = re.compile('\S+\s?')#匹配模式:非空白+空白=单词
        tmp_result = word_pattern.findall(string)
        return len(tmp_result)

        def line_count(self,string):
        '''
        文件行数统计、空行统计与代码行统计
        '''
        null_line = 0
        code_line = 0
        sepcial_pattern = re.compile('.*\n?')
        tmp_result = sepcial_pattern.findall(string)
        for lines in tmp_result:
            if len(lines.strip()) == 0 or (len(lines.strip()) == 1 and  lines.strip() in ['{','}','#','/','\\','(',')']):#规定中的空白行规则
                null_line += 1
            elif len(lines.strip())>=1 and lines[0]!= '/':
                code_line += 1
        return (len(tmp_result)-1,null_line-1,code_line)

    def single_comment_count(self,string):
        '''
        单行注释统计
        '''
        re_pattern = re.compile('//')
        del_pattern = re.compile('"[^"]*//[^"]*"')#排除双引号内情况
        multi_in_single_pattern = re.compile('/\*.*')#排除多行注释内情况
        tmp_result1 = re_pattern.findall(string)
        tmp_result2 = del_pattern.findall(string)
        for lines in tmp_result1:
            if len(multi_in_single_pattern.findall(lines)) == 1:
                self.multi_comment -= 1
        print('sig',len(tmp_result1) - len(tmp_result2))
        return len(tmp_result1) - len(tmp_result2)


    def multi_comment_count(self,string):
        '''
        多行注释统计
        '''
        multi_lines = 0
        del_lines = 0
        multi_pattern = re.compile('/\*+[^\*]+\*/')
        single_in_multi_pattern = re.compile('//.*')#排除单行注释内情况
        del_pattern = re.compile('"[^"]*/\*[^"]*\*/')#排除双引号内情况
        tmp_result = multi_pattern.findall(string)
        for result1 in tmp_result:
            self.single_comment -= len(single_in_multi_pattern.findall(result1))
            for x in result1:
                if x == '\n':
                    multi_lines+=1
        del_result = del_pattern.findall(string)
        for x in del_result:
            if x == '\n':
                del_lines += 1
        return multi_lines - del_lines

    def file_process(self,file):
        '''
        文件处理主函数
        '''
        with codecs.open(file, 'r', 'utf-8') as f:
            self.file_string = f.read()
        print('file:'+file,end=' ')
        if self.isnull(self.file_string):
            print('null file!')
            return
        else:
            self.multi_comment += self.multi_comment_count(self.file_string)
            self.single_comment += self.single_comment_count(self.file_string)
            self.char_num += self.char_count(self.file_string)
            self.word_num += self.word_count(self.file_string)
            (self.lines,self.nullline) = self.line_count(self.file_string)
            (self.lines,self.nullline,self.code_line) = self.line_count(self.file_string)
            if self.opt['a'] :
                print('character:'+str(self.char_num),end = ' ')
                print('words:'+str(self.word_num),end=' ')
                print('lines:'+str(self.lines),end=' ')
                print('code_line:'+str(self.code_line),end=' ')
                print('null_line:'+str(self.nullline),end=' ')
                print('comment_line:'+str(self.single_comment+self.multi_comment))
            else:
                if not (self.opt['c'] or self.opt['w'] or self.opt['l']):
                    print('Please input command\n')
                    return
                if self.opt['c']:
                    print('character:'+str(self.char_num),end = ' ')
                if self.opt['w']:
                    print('words:'+str(self.word_num),end=' ')
                if self.opt['l']:
                    print('lines:'+str(self.lines),end=' ')

    def file_select(self):#文件匹配
        print(self.opt['f'])
        file_list = glob.glob(self.opt['f'])
        for x in file_list:
            self.file_process(x)

图形界面类

from PyQt5.QtWidgets import *
from PyQt5.Qt import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from wc_class import word_counter


class wc_ui(QDialog):
    def __init__(self, parent=None):#控件初始化
        super().__init__()
        self.opt = opt = {'a':True}
        self.wc = word_counter(self.opt)
        self.setWindowTitle('Word Counter by Chernobyl')
        self.file_label = QLabel()
        self.is_null_label = QLabel('isnull:')
        self.character_label = QLabel('character:')
        self.word_label = QLabel('word:')
        self.line_label = QLabel('line:')
        self.null_line_label = QLabel('null_line:')
        self.code_line_label = QLabel('code_line:')
        self.comment_line_label = QLabel('comment_line:')
        self.mainlayout = QVBoxLayout()
        self.file_button = QPushButton('select file')
        self.file_button.clicked.connect(self.selectfile)

        self.mainlayout.addWidget(self.file_label)
        self.mainlayout.addWidget(self.is_null_label)
        self.mainlayout.addWidget(self.character_label)
        self.mainlayout.addWidget(self.word_label)
        self.mainlayout.addWidget(self.line_label)
        self.mainlayout.addWidget(self.null_line_label)
        self.mainlayout.addWidget(self.code_line_label)
        self.mainlayout.addWidget(self.comment_line_label)
        self.mainlayout.addWidget(self.file_button)

        self.setLayout(self.mainlayout)

    def selectfile(self):#选择文件,并获取所有信息,更新控件的显示值
        file_name = QFileDialog.getOpenFileName(self,"select file ","C:\\","c source files(*.c);;cpp source files(*.cpp);;header files(*.h)")
        self.wc.file_process(file_name[0])
        self.file_label.setText('file: %s'%(file_name[0]))
        self.is_null_label.setText('isnull: %s'%(str(self.wc.is_null)))
        self.character_label.setText('character: %d'%(self.wc.char_num))
        self.word_label.setText('word: %d'%(self.wc.word_num))
        self.line_label.setText('line: %d'%(self.wc.lines))
        self.code_line_label.setText('code_line: %d'%(self.wc.code_line))
        self.comment_line_label.setText('comment_line: %d'%(self.wc.single_comment+self.wc.multi_comment))
        self.null_line_label.setText('null_line: %d'%(self.wc.nullline))

运行截图

  • 按照题目要求所创建的5个文件——空白、一个字母、一个单词、一整行、普通文件。截图如下
    1482419-20180915000951069-107120254.jpg
  • 软件帮助界面如下
    1482419-20180915001310815-232301426.jpg

  • 使用ubuntu内置的wc工具结果如下。注意,猜测wc工具的匹配规则为结尾有换行符算作一行——这是个bug,因为示例一行文件中虽然结尾无换行符,但应算作一行
    1482419-20180915001150494-34164541.jpg
  • 使用自己实现的wc工具结果如图
    1482419-20180915001317375-1747360980.jpg
  • 图形界面截图如下
    1482419-20180915001331939-861542694.jpg
    1482419-20180915001337771-848593862.jpg

项目小结

  • 发现了在进行软件项目时的诸多问题,如进度管控、功能实现、debug等
  • 发现了wc工具的"bug"
  • 熟悉了python的图形界面编写和程序打包

转载于:https://www.cnblogs.com/Chernobyl/p/9644990.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值