作业题目链接

队友链接

Fork的同名仓库的Github项目地址

具体分工

玮哥负责命令参数判断、单词权重统计,我只负责词组词频统计(emmmm)。

PSP表格

预估耗时(分钟) 实际耗时(分钟)
Planning 计划
Estimate 估计这个任务需要多少时间 880 1170
Development 开发
Analysis 需求分析 (包括学习新技术) 100 120
Design Spec 生成设计文档 20 10
Design Review 设计复审 10 10
Coding Standard 代码规范 20 60
Design 具体设计 60 100
Coding 具体编码 400 510
Code Review 代码复审 400 510
Test 测试(自我测试,修改代码,提交修改) 100 180
Reporting 报告
Test Repor 测试报告 30 30
Size Measurement 计算工作量 20 15
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 60 45
合计 880 1170

解题思路描述与设计实现说明

爬虫使用

队友用Python编辑的爬虫代码。

import requests
from urllib.request import urlopen
from bs4 import BeautifulSoup
txt = open(r'C:\Users\Administrator\Desktop\result.txt','w',encoding='utf-8')
#打开文件
i = 0
def getPaper(newsUrl): #获取相关信息的函数
res = requests.get(newsUrl) #打开链接
res.encoding = 'utf-8'
soup = BeautifulSoup(res.text,'html.parser') #把网页内容存进容器
Title = soup.select('#papertitle')[0].text.strip() #找到标题元素爬取
print("Title:",Title,file=txt)
Abstract = soup.select('#abstract')[0].text.strip() #找到摘要元素爬取
print("Abstract:",Abstract,"\n\n",file=txt)
return sUrl = 'http://openaccess.thecvf.com/CVPR2018.py'
res1 = requests.get(sUrl)
res1.encoding = 'utf-8'
soup1 = BeautifulSoup(res1.text,'html.parser')
for titles in soup1.select('.ptitle'): #返回每个超链接
t = 'http://openaccess.thecvf.com/'+ titles.select('a')[0]['href']
print(i,file=txt)
getPaper(t) #循环打开每个子页面并进行爬取
i=i+1

代码组织与内部实现设计(类图)

  • 代码包含一个主要的类testfile以及实现功能的函数,类图如下所示。
  • Abstract和Title是分别存放摘要和标题内容的字符串。
  • outputByLine函数是在判断词组词频时能够按行返回文本内容并且剔除“Title: "和"Abstract: "。
  • phrasecounts分割词组并且计算词组数目与词频。
  • sWord结构体用作对词汇进行相关排序的容器。

算法的关键与关键实现部分流程图

命令行参数实现流程图

词组词频统计流程图

关键代码解释

命令行参数判断

while (1) //循环判断命令行参数
{
i++;
if (i == argc)
break;
if (argv[i][0] == '-') //判断当前参数首字符是否为'-'
{
switch (argv[i][1]) //判断当前参数第二个字符
{
case 'i':
{
i++;
input = argv[i];//第二个字符为i时输入下一个参数赋值给input
break;
}
case 'o':
{
i++;
output = argv[i];//第二个字符为o时输入下一个参数赋值给output
break;
}
case 'w':
{
i++;
judgeW = atoi(argv[i]);//第二个字符为w时judgeW为下一个参数的整数值留作输出判断
break;
}
case 'm':
{
i++;
judgeM = atoi(argv[i]);//第二个字符为m时judgeM为下一个参数的整数值留作输出判断
break;
}
case 'n':
{
i++;
judgeN = atoi(argv[i]);//第二个字符为n时judgeN为下一个参数的整数值留作输出判断
break;
}
default:
cout << "输入参数有误" << endl;
break;
}
}
}
配合命令行参数判断的输出代码如下: if (judgeW == 0)
{
if (judgeM == 0) //非权重词频统计
{
f1 = f1.countline(input, f1);
f1.Abstract = changeDx(f1.Abstract);
f1.Title = changeDx(f1.Title);//大小写转换
f1 = f1.countword(f1, f1.Title, 1);
f1 = f1.countword(f1, f1.Abstract, 1);
}
else //非权重词组词频统计
{
f1 = f1.outputByLine(input, f1, 1, judgeM);
}
}
else
{
if (judgeM == 0) //权重词频统计
{
f1 = f1.countline(input, f1);
f1.Abstract = changeDx(f1.Abstract);
f1.Title = changeDx(f1.Title);//大小写转换
f1 = f1.countword(f1, f1.Title, 10);
f1 = f1.countword(f1, f1.Abstract, 1);
}
else //权重词组词频统计
{
f1 = f1.outputByLine(input, f1, 10, judgeM);
}
} if (judgeN == 0)//如果没有定义n那么按默认输出
outCome1(ww, num, output, f1);
else
{
if (judgeN > f1.getwords())
{
cout << "输入的n值超过了文本的所有单词数,将为您按序输出文本的所有单词" << endl;//对于参数过大的错误判断
outCome2(ww, f1.getwords(), output, f1);
}
else
{
outCome2(ww, judgeN, output, f1);//如果定义了n那么输出前n个
}
}

词组词频统计

testfile testfile::phrasecounts(string temp, int t, int quan, testfile f1)
{
int word = 0;
int i = 0;
int j = 0;
long int n = 0;
int m = 0;
string sumwr = "";//初始化一个存放词组的字符串
n = temp.length();
char x[10];
for (j = 0; j < n; j++)
{
if (temp[j] >= 'A'&&temp[j] <= 'Z')
{
temp[j] += 32;
}
}
string phrase;
int flag = 0, k = 0;
int mark = 0, al = 0;//mark用来记录存储的单词数,al用来记录成词组的第二个单词的首字母序号
for (i = 0; i < n; i++)
{
if (!((temp[i] >= 48 && temp[i] <= 57) || (temp[i] >= 97 && temp[i] <= 122)))
{
if (mark > 0)
{
sumwr = sumwr + temp[i];//如果此时已记录一个及以上的单词,将此分隔符也录入词组字符串
}
continue;
}
else
{
for (j = 0; j < 4 && i < n; j++)
{ if (!((temp[i] >= 48 && temp[i] <= 57) || (temp[i] >= 97 && temp[i] <= 122)))
{
mark = 0;
sumwr = "";//检测到非法单词,重新初始化mark和词组字符串
break;
}
else
{
if (j == 0 && mark == 1)
{
al = i;
}
x[j] = temp[i++];//temp中存入四个非空格字符
}
}
if (j == 4)
{
for (m = 0; m < 4; m++)
{
if (x[m] < 97 || x[m]>122)
{
flag = 1;
mark = 0;
sumwr = "";
break;//判断这四个字符是否都是字母,检测到非法单词,重新初始化mark和词组字符串
}
}
if (flag == 0)//判断为一个单词
{
char *w = new char[100];//存放单词
for (m = 0; m < 4; m++)
{
w[k++] = x[m];//temp中字符存入w
}
while (((temp[i] >= 48 && temp[i] <= 57) || (temp[i] >= 97 && temp[i] <= 122)) && i < n)//继续存入单词剩余字符
{
w[k++] = temp[i++];
}
w[k] = '\0';
sumwr = sumwr + w;//将单词存入词组字符串数组
mark++;
delete[]w;
k = 0;
if (mark == t)
{
loadword(sumwr, quan);//如果此时单词存入达到所需数量,将此词组字符串存入map函数,并初始化mark和字符串
word++;
mark = 0;
i = al;//让i等于存入字符串的第二个单词的第一个字母序号,重新开始查询词组
sumwr = "";
}
i--;
}
else
{
flag = 0;
j = 0;
mark = 0;
sumwr = "";
}
}
}
}
i = 0;
f1.words += word;
return f1;
}

性能分析与改进

  • 输入的命令行参数为:-i C:\Users\Administrator\Desktop\result.txt -m 2 -n 20 -w 1 -o C:\Users\Administrator\Desktop\output.txt 即对爬取的文件进行词组分割,2个单词一组而后执行权重词频统计并且输出频率最高的前20个词组。
  • 性能探查器返回结果

  • 因为本次测试执行的是词组分割的词频统计,所以占比最高的函数就是词组分割函数phrasecount。其次本次程序执行时间在15秒到16秒左右,所以还有优化空间。loadword是把词组存入map,这是c++自带的一个统计词频并且按字典排序的容器,如果有能力的话希望自己能用哈希表写一个类似的排序也许能够提高运行效率。

单元测试

  • 以下对字符数统计,行数统计,单词数统计以及词组分割和词组词频统计四个函数进行单元测试。
#include "stdafx.h"
#include "CppUnitTest.h"
#include "C:\Users\Administrator\Desktop\软工实践\结对作业2\单元测试\WordCount2\WordCount2\WordCount2\WordCount2.h"
#include <iostream>
using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace WordCountTest1
{
TEST_CLASS(UnitTest1)
{
public: TEST_METHOD(phraseCount)
{
testfile f1;
f1=f1.outputByLine("C:\\Users\\Administrator\\Desktop\\result.txt", f1, 1, 3);//按行输出后进行词组词频统计
}
TEST_METHOD(wordCount)
{
testfile f1;
f1 = f1.countline("C:\\Users\\Administrator\\Desktop\\result.txt", f1);
f1 = f1.countword(f1, f1.Abstract, 1);
f1 = f1.countword(f1, f1.Title, 1);
}
TEST_METHOD(countCharacters)
{
testfile f1;
string a;
f1 = f1.countcha("C:\\Users\\Administrator\\Desktop\\result.txt", f1);
Assert::AreEqual(f1.getcharacters(), (int)1220332);
}
TEST_METHOD(countLine)
{
testfile f1;
f1 = f1.countline("C:\\Users\\Administrator\\Desktop\\result.txt", f1);
Assert::AreEqual(f1.getlines(), (int)2937);
}
};
}
  • 单元测试通过

单元测试列表:

  • 观察单元测试图可以看到,在对一百万字符数的文本文档进行统计时,总执行时间要20秒,其中词组分割花费11秒最多,再者就是单词分割,需要6秒。还有很大的优化空间啊。

Github签入记录

  • 共签入了三次。第一次是自己做的那块功能做完后签入。而后队友做的功能完善后,整理合并到代码里再次签入。第三次签入时进行完单元测试并加入爬虫结果。

遇到的代码模块异常或结对困难及解决方法

  • emmm我负责的是词组词频统计的代码,一开始做的时候甚至理解错了题目的意思,将m(词组单词数)理解成了分隔符的数量,幸好玮哥很早就发现了这个问题,我才有时间得以重做。
  • 我设计的词组词频统计起初是一篇文本为目标,但是这样的话,会将换行符也当成分隔符存入词组字符串,而且不好区分和消除title和abstract的干扰,于是我便想到一行一行的统计,因为原文中,title及其内容为一行,abstract及其内容也是一行,玮哥写的按行读入代码又有erase函数在读入一行的同时削去了title:和abstract:,把这样读入的一行带入我词组词频统计代码,既能消除换行符和title、abstract的影响,又能减少一次带入函数的数据量,防止数据过多而出现的代码运行错误。

评价你的队友

   玮哥是我大腿,除了词组词频统计的代码,其他的像是什么代码整合,单元测试七七八八的都是由他一个人完成,中途还因为我的失误浪费了一天的时间(有点小愧疚)。下次合作任务时,争取做到细心再细心,像玮哥一样多帮队友分担一些的任务。

学习进度条

第n周 新增代码 本周学习耗时 重要成果
3 400 12h 对于字符串的处理更加精进了

结对第2次作业——WordCount进阶需求的更多相关文章

  1. 结队第二次作业——WordCount进阶需求

    结队第二次作业--WordCount进阶需求 博客地址 051601135 岳冠宇 博客地址 051604103 陈思孝 博客地址 Github地址 具体分工 队友实现了爬虫功能,我实现了wordco ...

  2. 《软件工程实践》第五次作业-WordCount进阶需求 (结对第二次)

    在文章开头给出结对同学的博客链接.本作业博客的链接.你所Fork的同名仓库的Github项目地址 本作业博客链接 github pair c 031602136魏璐炜博客 031602139徐明盛博客 ...

  3. 第二次结对作业-WordCount进阶需求

    原博客 队友博客 github项目地址 目录 具体分工 需求分析 PSP表格 解题思路描述与设计实现说明 爬虫使用 代码组织与内部实现设计(类图) 算法的关键与关键实现部分流程图 附加题设计与展示 设 ...

  4. 软工实践第五次作业-WordCount进阶需求

    软工实践作业(五) GitHub 作业链接 结对博客 031602240 具体分工 PSP表格 代码规范 解题思路与设计说明 爬虫使用 代码组织与内部实现设计(类图) 算法关键 实现方法 流程图 附加 ...

  5. 软工实践——结对作业2【wordCount进阶需求】

    附录: 队友的博客链接 本次作业的博客链接 同名仓库项目地址 一.具体分工 我负责撰写爬虫爬取信息以及代码整合测试,队友子恒负责写词组词频统计功能的代码. 二.PSP表格 PSP2.1 Persona ...

  6. 结对作业-WordCount进阶版

    1.在文章开头给出博客作业要求地址. 博客园地址:https://www.cnblogs.com/happyzm/p/9559372.html 2.给出结对小伙伴的学号.博客地址,结对项目的码云地址. ...

  7. 结对作业——WordCount进阶版

    Deadline: 2018-10-7 22:00PM,以博客提交至班级博客时间为准 要求参考来自:https://www.cnblogs.com/xinz/archive/2011/11/27/22 ...

  8. 福州大学软件工程1816 | W班 第6次作业WordCount成绩排名

    作业链接 WordCount进阶需求 评分细则 本次个人项目分数由两部分组成(博客分满分40分+程序得分满分60分) 博客评分细则(满分60,最终折算为40分) 在文章开头给出结对同学的博客链接.本作 ...

  9. 《ABCD组》第四次作业:项目需求调研与分析

    <ABCD组>第四次作业:项目需求调研与分析 项目 内容 这个作业属于哪个课程 http://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https:// ...

随机推荐

  1. oracle中如何创建表的自增ID(通过序列)

    1.什么是序列呢? 序列是一数据库对象,利用它可生成唯一的整数.一般使用序列自动地生成主码值.一个序列的值是由特别的Oracle程序自动生成,因而序列避免了在运用层实现序列而引起的性能瓶颈. Orac ...

  2. 蓝桥杯—BASIC-19 完美的代价(贪心)

    问题描述 回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的.小龙龙认为回文串才是完美的. 现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串. 交换的定义 ...

  3. Spring MVC之DispatcherServlet初始化详解

    Spring作为一个优秀的web框架,其运行是基于Tomcat的.在我们前面的讲解中,Spring的驱动都是使用的ClassPathXmlApplicationContext,并且都是直接在main方 ...

  4. 《Python》网络编程之验证客户端连接的合法性、socketserver模块

    一.socket的更多方法介绍 # 服务端套接字函数 s.bind() # 绑定(主机,端口号)到套接字 s.listen() # 开始TCP监听 s.accept() # 被动接受TCP客户的连接, ...

  5. Android : 代码多维度管理(产品风味) - flavorDimensions & productFlavors

    一.关于配置产品风味 Android studio 升级到3.0之后,gradle增加了多维度管理配置,便于同一个项目中创建应用的不同版本,分别管理依赖项并签署配置.创建产品风味与创建构建类型类似:只 ...

  6. 6.3 C++修改字符串

    参考:http://www.weixueyuan.net/view/6392.html 总结: string字符串同样可以像字符串数组那样按照下标逐一访问字符串中的每一个字符,string字符串的起始 ...

  7. Mysql使用information.shema.tables查询数据库表大小

    简介: information_schema数据库中的表都是只读的,不能进行更新.删除和插入等操作,也不能加触发器,因为它们实际只是一个视图,不是基本表,没有关联的文件. 元数据描述数据的数据,用于描 ...

  8. mybatis学习(一)----入门

    一.Mybatis介绍 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名 ...

  9. 項目当中使用的easyui的模板crud页面

    1.简单的增删改查页面: 第一步骤:html原型型编写,写法参照了easyui demo中form有关html等 <!DOCTYPE html> <html> <head ...

  10. synchronized(九)

    在Java中是有常量池缓存的功能的,就是说如果我先声明了一个String str1 = “a”; 再声明一个一样的字符串的时候,取值是从原地址去取的,也就是说是同一个对象.这也就导致了在锁字符串对象的 ...