这个作业属于那个课程 | 构建之法-2021年秋-福州大学软件工程 |
---|---|
这个作业要求在哪里 | 2021年秋软工实践第一次个人编程作业 |
这个作业的目标 | 实现一个程序功能,它可以对读入的 C或C++代码文件进行不同等级的关键字提取;学会使用 Git 进行版本控制 |
学号 | 031902110 |
文章目录
一、PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 45 | 60 |
Estimate | 估计这个任务需要多少时间 | 30 | 25 |
Development | 开发 | ||
Analysis | 需求分析 (包括学习新技术) | 200 | 250 |
Design Spec | 生成设计文档 | ||
Design Review | 设计复审 (审核设计文档) | ||
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 60 | 75 |
Design | 具体设计 | 60 | 200 |
Coding | 具体编码 | 1680 | 2000 |
Code Review | 代码复审 | 100 | 100 |
Test | 测试(自我测试,修改代码,提交修改) | 180 | 240 |
Reporting | 报告 | ||
Test Report | 测试报告 | ||
Size Measurement | 计算工作量 | 60 | 60 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 90 | 90 |
合计 | 2505 | 3100 |
二、解题思路
当拿到题目的时候,分析题目要求。题目共分为 4 个要求,逐步实现每个要求,先从基础的开始。
1. 文本的读取及处理
- 先把所有的标点符号都找出来,再用空格来替代标点符号,最后再以空格作为分隔符来分隔单词。
2. 基础要求
- 创建一个关键字集合
- 设置一些过滤条件过滤非关键字,将关键字保存到列表中
- 统计关键字的个数并输出
3. 进阶要求
- 创建一个列表存储每个 swtich 对应的 case 个数
- 利用基础要求中获得的关键字列表,对列表做一次遍历
- 当遇见 switch 从 0 开始记录 case 的个数直到遇见下一个 swtich 或关键字列表遍历结束
- 输出 switch 和 case 的信息
4. 拔高和终极要求
- 这两个要求相关性很大,所以放到一起完成
- 把 else if 合并成 elseif
- 遍历关键字列表,遇见 if、elseif (多个只需入栈一次)就将其入栈(用列表模拟栈)
- 遇到 else 时查看栈顶元素,若为 if 则将其出栈,if else 结构数量加 1
- 若栈顶为 elseif 则做出栈操作两次,if,else if,else结构数量加 1
三、设计实现过程
1. 代码组织
2. 函数流程图
四、代码说明
1. 文本读入函数
def get_text():
file = open(sys.argv[1], "r", encoding="UTF-8") # 用 UTF-8 格式打开防止乱码
txt = file.read()
for ch in '!#$%&()+,-.:;<=>?@[\\]^_{|}~':
# 考虑注释和引号,不能去掉符号 * / “
txt = txt.replace(ch, " ") # 将符号替换成空格
file.close()
return txt
2. 去除注释、双引号字符串
def words_filter():
txt = get_text().replace("else if", "elseif")
separator = [r'//.*', r'\/\*(?:[^\*]|\*+[^\/\*])*\*+\/', r'".*"']
# 分别以 //单行注释 /* 多行注释*/ "引号" 作为分隔符
for sp in separator:
wordlist = re.split(sp, txt) # 运用正则表达式匹配分隔文本
txt = ""
for word in wordlist: # 重新拼接字符串
txt = txt + word
wordlist = txt.split()
return wordlist
words = words_filter()
filterwords = []
3. 统计关键字个数
def first_level():
keywords = {"auto", "break", "case", "char", "const",
"continue", "default", "do", "double", "else",
"enum", "extern", "float", "for", "goto",
"if", "int", "long", "register", "return",
"short", "signed", "sizeof", "static", "struct",
"switch", "typedef", "union", "unsigned",
"void", "volatile", "while", "elseif"
} # 把 else if 合并 成 elseif 作为一个关键字,为拔高和终极要求做准备
counts = {} # 关键字字典
cnt = 0
for word in words:
# 设置条件过滤字符串,保留关键字
if len(word) == 1 or (word not in keywords):
continue
counts[word] = counts.get(word, 0) + 1
filterwords.append(word)
cnt = cnt + 1
cnt = cnt + counts.get("elseif", 0) # elseif 合并后要多算一次
print("total num: {}".format(cnt))
return counts
4. 计算 switch case 个数
def second_level(counts):
num = counts.get("switch", 0)
print("switch num: {}".format(num))
if num == 0:
print("case num: {}".format(num))
return
count = []
flag = -1
for word in words:
if word == "switch": # 遇到 switch, count[]列表元素加 1
count.append(0)
flag += 1
elif word == "case":
count[flag] += 1
else:
continue
print("case num: ", end="")
print(" ".join(str(x) for x in count))
5. 计算 if else 和 if, elseif, else
def last_level():
stack = []
if_else_num = 0
if_elseif_else_num = 0
for word in filterwords:
if word == "if": # 遇到 if 就把它入栈
stack.append(word)
elif word == "elseif" and stack[-1] != "elseif": # 连续 elseif 入栈一个就行
stack.append(word)
elif word == "else": # 遇到 else 查看栈顶关键字
if stack[-1] == "if": # 是 if 直接出栈,if else 结构加 1
stack.pop()
if_else_num += 1
elif stack[-1] == "elseif": # 栈顶是 elseif 连续出栈两个元素
stack.pop()
stack.pop()
if_elseif_else_num += 1
print("if-else num: {}".format(if_else_num))
if sys.argv[2] == '4':
print("if-elseif-else num: {}".format(if_elseif_else_num))
五、单元测试截图和描述
单元测试结果和预期一致
六、性能测试
七、总结
-
作业收获
- 通过这次作业,我学习了一些新的 python 技能,如:从命令行执行程序并读取参数、正则表达式的使用、单元测试、性能分析。
- 学会了用 Git 来控制版本库,每一次的 commit 都能收获一点点的成就感。
- 学会将本地仓库与远程的 GitHub 仓库关联起来,明白了 Git 这种分布式管理的优点。
- 能够对目标进行简单规划
-
遇到的困难及解决
- 最大的困难就是对时间的把控,很难完全按照制定好的时间规划做任务。对此我的解决方法是不要把时间一下规划得太长,多将时间分片,这样做任务的时候不容易中断