第二次作业

软工第二次作业

1.Github地址

2. Personal Software Process

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划6801285
· Estimate· 估计这个任务需要多少时间6801285
Development开发5501135
· Analysis· 需求分析 (包括学习新技术)120270
· Design Spec· 生成设计文档208
· Design Review· 设计复审100
· Coding Standard· 代码规范 (为目前的开发制定合适的规范)200
· Design· 具体设计607
· Coding· 具体编码270600
· Code Review· 代码复审3040
· Test· 测试(自我测试,修改代码,提交修改)20210
Reporting报告130150
· Test Repor· 测试报告6040
· Size Measurement· 计算工作量1020
· Postmortem & Process Improvement Plan· 事后总结, 并提出过程改进计划6090
合计6801285

3.程序的分析实现

  拿到题目后,我首先发现题目要求在命令行窗口中输入应用程序名以及需要扫描的文档。这在从前的题目中没遇到过。开始就来一些新鲜的东西,挺有趣。之后题目中有五个要求:

  • 一:统计文件的字符数:
    • 只需要统计Ascii码,汉字不需考虑
    • 空格,水平制表符,换行符,均算字符
  • 二:统计文件的单词总数,单词:至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写:
    • 英文字母: A-Z,a-z
    • 字母数字符号:A-Z, a-z,0-9
    • 分割符:空格,非字母数字符号
  • 三:统计文件的有效行数:任何包含非空白字符的行,都需要统计。
  • 四:统计文件中各单词的出现次数,最终只输出频率最高的10个。频率相同的单词,优先输出字典序靠前的单词。
  • 五:输出的单词统一为小写格式。

  这五个要求看起来不难,但想一想确实并不容易。

  首先我应该考虑该如何从比如“input.txt”的文档中获取文本。经查阅资料发现可以借助fopen(char c,”r”)函数。通过读取c中的路径来打开txt文档,后返回一个FILE指针,我们命名为fp。后调用getc()函数,即通过getc(fp)来获取txt中的每个字符,每读一次,指针后移一位,指向下一字符。返回值可以用一个char字符来接受。

  其次该如何将结果输出到一个文档呢?经查阅资料发现,可以通过头文件fstream中的函数实现。比如定义ofstream类型的对象,命名为outfile。定义的同时可以直接定义路径,如:ofstream outfile("output.txt")。意思是将结果输出到output.txt的文件中,为了保险起见我写全了文件路径。之后可以用<<来输出字符,比如:outfile<< "Hello" 就会在文档中输出Hello。不过函数默认覆盖源文档内容。如需接在文末,可以在定义时加上ios::app,比如:ofstream outfile("output.txt",ios::app)。至此解决了输入输出问题。

  之后统计字符数,我写了一个charcount函数实现。首先通过while循环来控制何时结束读取,比如:while (!feof(fp)),作用是当指针不指向文档末尾,那么就仍然while循环。其中feof(FILE *fp)作用是当fp指向文档末尾即返回0,否侧返回1。综合以上我就想到,每次调用一次getc()函数,字符数就+1.关键代码如下:

    while (!feof(fp))
    {
        char c = getc(fp);
        charcnt++;
    }

  行数是一个难点,我写了一个linecount函数实现。意思是,仅当一行中出现非空格且非换行的时候,才算真正的一行。因此可以想到这种方法:先立flag=1,之后碰见如上行且flag==1时才使得行数+1,之后马上置flag=0。当遇见下一个换行符时,置flag=1。如此循环,即可跳过那些无效行。注意其中EOF的排除很关键!关键代码如下:

    while (!feof(fp))
    {
        char c = getc(fp);
        if ((c >= 'a'&&c <= 'z') || (c >= 'A'&&c <= 'Z'))//可能是单词 
        {
            k = 0;//从第0位开始判断
            while ((c >= 'a'&&c <= 'z') || (c >= 'A'&&c <= 'Z') || (c >= '0'&&c <= '9'&&k >= 4))//继续后几位的验证 
            {
                if (c >= 'A'&&c <= 'Z')//大写改小写
                {
                    c = c + 32;
                }
                temp.s[k] = c;
                k++;//下一位

                c = getc(fp);
            }
            temp.s[k] = '\0';//结束标识 
            k++;//此词位数+1

            j = n;

            if (strlen(temp.s) >= 4)//确保大于4位英文字母 
            {
                wordcnt++;
                temp.frq = 1;//词频置1

                for (i = 0; i < j; i++)
                {
                    if (strcmp(temp.s, word[i].s) == 0)//归于之前的 
                    {
                        word[i].frq++;
                        break;
                    }
                }

                if (n == 0 || i == j)//新的另外处理
                {
                    word[n] = temp;
                    n++;
                }
            }
        }
    }

  最终只输出词频最大的前十个,若词频相同,则按其字典序列输出。之前我想的是:先整理出词频最大的前10个,用一个结构体数组max来存,然后再将其中词频相同的按其字典序列输出。当然之后我发现其中有问题,就是在词频相同的单词中,某些在字典序列排名靠前的单词不会出现在max数组中!迫于能力有限,我想了一种时空开销很大的办法,即:先对word数组按字典顺序排列,再将它们按词频排序。排序方法都选择冒泡排序。

  因为单词我用char数组来存。。。这就有点麻烦。若是string可以直接用>,<,=来判断序列顺序。那么我想到用冒泡排序来实现,对于每个结构体数组中的单词,对单词每位逐一判断来实现。虽然这样时间开销灰常大,O(n3)。我写了一个sor函数实现,关键代码如下:

void  sor(wd *f, int n)//wd是结构体
{
    int w;
    for (int i = 0; i<n - 1; i++)
        for (int j = 0; j < n - i - 1; j++)//冒泡排序思想
        {
            w = 0;
            while (f[j].s[w] == f[j + 1].s[w])
                w++;
            if (f[j].s[w] > f[j + 1].s[w])//ASCII码小的在前面
                swap(f[j], f[j + 1]);
        }
}

  然后按词频排序,思想方法都与上面的相同,不再叙述。关键代码如下:

    for (i = 0; i < 10; i++)//初始化前十名
    {
        max[i] = word[i];
    }

    for (j = 0; j < 10; j++)//前十名排序 
    {
        for (i = 0; i < 10 - j - 1; i++)
        {
            if (max[i].frq < max[i + 1].frq)
            {
                swap(max[i], max[i + 1]);
            }
        }
    }

    for (i = 10; i < n; i++)//找后面的数 
    {
        if (max[9].frq < word[i].frq)//比最小的还大 
        {
            int a = 8;
            while (max[a].frq < word[i].frq&&a >= 0)
            {
                a--;//定位到第一个比自己大的word
            }

            for (j = 9; j > a + 1; j--)
            {
                max[j] = max[j - 1];//前面的数后移一位 
            }
            if (a < 0)//说明word[i]比max[0]大
                max[0] = word[i];
            else
                max[j] = word[i];
        }
    }

4.总结心得

  整个过程中我都没有。。。画流程图或者考应该如何组织代码。以前没有养成这个习惯,下次会注意。

  刚开始写的时候,我的函数互相杂糅,没有清楚的界限。这导致了我后来写接口就很麻烦了。因为之前的代码习惯很不好,喜欢把所有的东西都写在main函数里,在后来的修改过程中我吸取了教训,把每一个单独的功能模块都隔离出来,封装成了单独的函数。这样代码结构很清晰,但同时代码冗余就很大,我不知道该如何克服这一点?

  这次的作业量对我来说是前所未有的大。其中我学习到了编写代码仅是一个项目中较小的一部分,大概20%~30%左右吧。更多的是养成良好的编码习惯,了解接口的重要性,以及在之后对程序的调试,性能测试。同时自己得到了许多提升,希望下次能有更大的进步!

  接下来是我的单元测试代码,迫于时间,真的很偷懒而且不充分。。。:

#include "stdafx.h"
#include "CppUnitTest.h"
#include "C:\\Users\我\Desktop\test1\test1\linecount.h"
#include "C:\\Users\我\Desktop\test1\test1\charcount.h"
#include "C:\\Users\我\Desktop\test1\test1\wordcount.h"
#include "C:\\Users\我\Desktop\test1\test1\wordmaxsort.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace UnitTest1
{       
    TEST_CLASS(UnitTest1)
    {
    public:
        
        TEST_METHOD(TestMethod1)
        {
            char *c = "H:\\最新.txt";
            int charcnt = 130, linecnt = 8, wordcnt = 18;
            Assert::IsTrue(0 == 0);
            Assert::IsTrue(charcount(c) == charcnt);
            Assert::IsTrue(linecount(c) == linecnt);
            Assert::IsTrue(wordcount(c) == wordcnt);
        }

    };
}

侧视图如下: 单元测试

  性能分析图如下:
性能分析1
性能分析3
性能分析4

转载于:https://www.cnblogs.com/Tony-chenjw/p/9637977.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值