作业题目链接

队友链接

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. puppeteer 爬虫 pdf 截图 自动化

    puppeteer简介 puppeteer 翻译是操纵木偶的人,利用这个工具,我们能做一个操纵页面的人.puppeteer是一个nodejs的库,支持调用Chrome的API来操纵Web,相比较Sel ...

  2. pycharm开发工具,使用

    在pycharm中,打的断点,仅在调试模式下,即debug 模式下,才有效 Use Alt + Shift + C to quickly review your recent changes to t ...

  3. 服务消费和负载(Ribbon)

    使用RestTemplate调用服务 在上一篇教程中,我们是这样调用服务的,先通过 LoadBalancerClient 选取出对应的服务,然后使用 RestTemplate 进行远程调用. Load ...

  4. 启动项目时出现java.io.EOFException异常。

    错误: 2018-4-18 10:55:54 org.apache.catalina.session.StandardManager doLoad 严重: IOException while load ...

  5. tomcat访问端口

    问题描述:今天,访问服务器上的应用,tomcat服务器已经启动,确怎么都打不开:问题原因:原来有人改了端口.具体操作:进入tomcat的安装目录,进入conf文件夹下,找到server.xml文件.用 ...

  6. Python Django 之 基于JQUERY的AJAX 登录页面

    一.基于Jquery的Ajax的实现 1.url 2.vews 3.templates

  7. Struts1的基础知识

    struts1.0的配置 在web.xml文件中的配置 <servlet> <!--配置ActionServlet类,一启动就创建该类对象--> <servlet-nam ...

  8. IIS设置上传文件大小限制

    单位为字节. 500*1024*1024=524288000

  9. java的类class 和对象object

    java 语言的源代码是以类为单位存放在文件中,已public修饰的类名须和存放这个类的源文件名一样.而 一个源文件中只能有一个public的类,类名的首字母通常为大写. 使用public修饰的类可以 ...

  10. Cracking The Coding Interview5.1

    //You are given two 32-bit numbers, N and M, and two bit positions, i and j. Write a method to set a ...