作业题目链接

队友链接

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. easyui中如何为validatebox添加事件(onblur、onclick等)

    在我们一般html的input标签,textbox事件可以直接使用onblur().onclick()事件,但是在easyui的validatebox没有onblur事件, 如果我们需要为valida ...

  2. linux的命令:

    uname -r  linux的版本号 uname -a  显示系统名.节点名称.操作系统的发行版号.操作系统版本.运行系统的机器 ID 号 cd /dev/ 切换到根目录: ls 查看根目录文件

  3. php对于url提交数据的获取办法

    $url = Request::getUri();//获取当前的url $arr = parse_url($url); //$arr_query = convertUrlQuery($arr['que ...

  4. linux系统管理 计划任务

    一次性计划任务 命令: at 语法: at [-f 文件名] 时间 绝对计时方法 HH:MM yyyy-MM-dd 相对计时方法 now + n minutes now+n hours now + n ...

  5. Object value iterator:值迭代器

    // ES5 // 对于数组,使用标准的 for 循环,其实这种方式并不是在遍历值,而是遍历下标来指向值!ES5中增加了一些数组迭代器:forEach.every.some // forEach:遍历 ...

  6. 如何在Ubuntu中安装中文输入法

    在使用ubuntu系统时,有的时候总觉得英文输入法不方便操作,总希望能有中文输入法可以辅助操作,那怎样才能在ubuntu中安装中文输入法呢?下面有一种简单的方法可以安装中文输入法. 如何在ubuntu ...

  7. C++基础知识:STL简介

    1.标准模板库STL ― STL , 即 : Standard Template Library , 是 C++的一部分― STL是常用数据结构和算法的集合― STL的目标是标准化组件 , 提高开发效 ...

  8. 打开和写入word文档

    一. 使用win32读取word内容 # -*- coding: utf-8 -*- from win32com import client as wc def readDocx2(): word = ...

  9. centos7 安装jdk8 bash脚本 并配置环境变量

    #!/bin/bash #安装java脚本 if type -p java; then echo 'java已安装.' exit else echo '开始安装java...' wget --no-c ...

  10. 强化学习3-蒙特卡罗MC

    之前讲到强化学习可以用马尔科夫决策过程来描述,通常情况下,马尔科夫需要知道  {S A P R γ},γ是衰减因子,那为什么还需要蒙特卡罗呢? 首先什么是蒙特卡罗? 蒙特卡罗实际上是一座赌城的名字,蒙 ...