zoukankan      html  css  js  c++  java
  • 2017秋-软件工程第二次作业

    本周因为个人缘故,参加社团活动作业没能及时完成。对此我表示,做过就不后悔,至少我觉得生活是丰富多彩的,错过的时间就应该努力赶上!夜深人静的时候总是可以让人反省自己。本次作业我只实现了第一个功能和第二个功能的部分。对此我表示很不满,但是时间紧迫、个人能力有限,以至于自己没能让自己的软件看起来完美。

    第二次作业的内容非常有趣,这也是我一直想做的一件事情,统计一篇文章里的字词。我知道自己的编程能力较差、距离完成提交时间很近,自己手写全部是不能及时按照约定提交的,于是就尝试借鉴前人的代码。第一晚的努力各种报错的冲击下化为无用功,因为第一晚所修改的代码不可读取大文件。我对其中一些语句的理解还有所不足,所以第二天就没再使用。第二天我从51网站上下载了一份有关“词频统计”功能的代码来使用。老师提到希望我们使用c#语言,因为前一周及周末都没有花时间学习,对新鲜语言感到陌生和抗拒,又因为不想安装jave环境,所以只好选用c++来开发。第一晚安装了visualstadio,新建项目运行代码。

    任务一,主要有2部分:1读取示例文本文件中的文字,2对该文本中的文字进行数量统计。但是我觉得本部分难点在于如何在控制台,使用命令行语句,操作程序。先上图,查看完成结果。

    因为在命令行执行,这个是我不熟悉的地方。开始的时候我是不明白的。包括那个“type”的功能也不知道。第一项比较简单。

    第二项作业我遇到了很多难题:包括自己手打的txt就出错,老师示例样本中的却不出错(开始的时候我没有下载到老师的测试用例,而是复制全部拷贝)、我还修改了txt的字符格式。大文件的读取也是一个问题,在第一晚尝试的程序中不能很好的读取全部内容(所以舍弃不用),之后就是一些小问题,我一一解决。如下展示部分关键代码。

    该部分代码用于显示输出内容,格式控制的主要部分。

     1 void Display_for_softwareclass(list<Word> &lWord)
     2 {
     3     list<Word>::iterator iteBegin = lWord.begin();
     4     list<Word>::iterator iteEnd = lWord.end();
     5     list<string> word;
     6     for (; iteBegin != iteEnd; iteBegin++)
     7     {
     8         word.push_back((*iteBegin).GetWordInfor());
     9     }
    10     word.sort();
    11     word.reverse();
    12     int icount = 0;
    13     string sTemp;
    14     list<string>::iterator wordBegin = word.begin();
    15     list<string>::iterator wordEnd = word.end();
    16     sTemp = *wordBegin;
    17     int total = 1;//用于记录不同的单词的数目
    18 
    19     for (; wordBegin != wordEnd; wordBegin++)
    20     {
    21         if (sTemp == *wordBegin)
    22         {
    23             icount++;
    24             continue;
    25         }
    26         total++;
    27         if (sTemp != *wordBegin)
    28         {
    29             sTemp = *wordBegin;
    30             icount = 1;
    31         }
    32     }
    33     cout <<"total "<< total << endl << endl;
    34     word.sort();
    35     word.reverse();
    36     icount = 0;
    37     wordBegin = word.begin();
    38     sTemp = *wordBegin;
    39 
    40     cout << left << setw(15) << sTemp;
    41     for (; wordBegin != wordEnd; wordBegin++)
    42     {
    43         if (sTemp == *wordBegin)
    44         {
    45             icount++;
    46             continue;
    47         }
    48         cout << right << setw(3) << icount << endl;
    49         //total++;
    50         if (sTemp != *wordBegin)
    51         {
    52             sTemp = *wordBegin;
    53             cout << left << setw(15) << sTemp ;
    54             icount = 1;
    55         }
    56     }
    57     cout<< right << setw(3) << icount << endl;
    58     cout << endl << endl;
    59}

    这段代码展示的是消耗我时间最长的一部分代码。其中主要的问题来自数据类型!!!目前来讲,通过命令行控制程序这个功能我知道怎么实现,但在本程序中没有体现。

     1 /*
     2  inline char* UnicodeToAnsi(const wchar_t* szStr)
     3 {
     4  int nLen = WideCharToMultiByte(CP_ACP, 0, szStr, -1, NULL, 0, NULL, NULL);
     5  if (nLen == 0)
     6  {
     7     return NULL;
     8  }
     9  char* pResult = new char[nLen];
    10  WideCharToMultiByte(CP_ACP, 0, szStr, -1, pResult, nLen, NULL, NULL);
    11  return pResult;
    12  }
    13 char TcharToChar(const TCHAR * tchar)
    14 {
    15     int iLength;
    16     char res;
    17     char * _char;
    18     //获取字节长度   
    19     iLength = WideCharToMultiByte(CP_ACP, 0, tchar, -1, NULL, 0, NULL, NULL);
    20     //将tchar值赋给_char    
    21     res = WideCharToMultiByte(CP_ACP, 0, tchar, -1, _char, iLength, NULL, NULL);
    22     return res;
    23 }
    24 */
    25 int _tmain(int argc, _TCHAR* argv[])
    26 {
    27     list<Word> lWord;
    28     string fileName;
    29     cout << ">wf -s ";        //这两行完全是为了凑格式写的
    30     cin >> fileName;
    31     OpenFile(fileName, lWord);
    32     Display_for_softwareclass(lWord);
    33 /*
    34         wcout << argc << endl;
    35         for (int i = 0; i < argc; i++)
    36         {
    37             wcout << "argv[" << i << "]=" << argv[i] << endl;
    38         }
    39         char argv_char[100] = "";
    40         char argv_char_result[100] = "";
    41         argv_char_result[100] = TcharToChar(argv[2]);
    42         string fileName;
    43         fileName = argv_char_result[100];
    44         cout << "文件名字是:"<<fileName;
    45         OpenFile(fileName, lWord);
    46         Display_for_softwareclass(lWord);
    47         */
    48     return 0;
    49 }

    在命令行中,运行“wf”文件,传递的第一个参数是“-s”,第二个参数是“test.txt”。“-s”是参数?还是什么?使用不同语言的同学给了我各种各样的回答,简单说几个:某个参数的形参、无意义的参数,空过去不用就好、也许是运行程序中预期的某种功能s,lunix系统下的某种用法。至此,我想听一下老师的解答。其实不是我“不想问”,而是我“不会问”。我不知道怎么描述这个问题,不知道这个是有关那一部分的知识,怎么提问。

    在一段时间的学习后,我大概知道了其中的含义,在使用控制台运行时,main()函数是可以接收参数的!它不仅仅是一个函数的名称了。开始的时候我是高兴的,因为通过尝试小例子,我可以愉快的读写main()函数中传入的参数。但是当具体写入我的程序的时候,我就很麻烦:各种错误。最大的问题来自这里:

    int _tmain(int argc, _TCHAR* argv[]){}

    _tmain和main的区别,char和_TCHAR*的区别?我希望使用_TCHAR* 这样的数据类型的字符,将它转化为string类型的即可当作文件名使用,但是强制转换过程中出现了问题。我还尝试使用main和char代替他们,也出现了问题。出现的问题对我来说,无法理解,感觉不好解决,经过百度,我得到了很多目前我觉得很闹心的答案,好几次出现“类似的”问题,但是百度得到的答案却不同:内存出错、内存冲突、空指针、内存空间不足、叫我们调用堆栈查看内部获取值。我也看到了获取了非法的数值,应该出现1个单词的地方出现了一行,但是我却不知道怎么找到对应解决办法。

    如下,我想问老师一些问题:

    1我们是应该先仔细看别人的代码,再模仿写自己?还是从头开始写自己的,不会哪里找哪里?

    2遇到那些,不知道怎么解决的问题怎么问?我们应该怎样描述所遇到的问题?去哪里问?我们怎样较快的查找到相关的解答?

    3抄袭可耻,但是复制代码属于抄袭么?怎么算这个软件是我自己写的,还是抄别人的?

    4怎样看待那些莫名其妙的问题?比如自己手写输入的测试用例就不好用,反而下载的好用?那我怎么确定是样本不好,还是我的程序不好呢?

    任务一小结:完成了读取文本文件内容并输出的功能,完成了对一些符号的识别和区分。未完成大小写的统一、未完成从命令台输入指令传递参数的功能。

    任务二

    任务二是任务一的升级,要求读入大量的数据并统计单词量。我完成了统计每个单词数量、单词总个数的功能,完成了排序功能,如下是效果展示图和代码。

     采用的是《战争与和平》大概3.5MB.很多程序不能运行大文件,本程序可以读取大文件。如下代码是打开文件读取数据部分代码。

    void OpenFile(const string fileName,list<Word> &lWord)
    {
        //list<Word> lWord;
        int paragraphNum = 1;
        int sentenceNum = 1 ;
        int wordNum = 1;
        ifstream fin(fileName.c_str());
        if(!fin)
        {
            cout<<"File open error!
    ";
            return;
        }
        char ch;
        while(fin.get(ch))
        {
            if(ch=='
    ')
            {         
                paragraphNum++;
                sentenceNum=1;
                wordNum=1;
            }
            else
            {
                char temp[50];
                int icount=0;
                while((ch !='
    ')&&(ch !='.')&&(ch !='!')&&(ch !='?')&&(ch !=' ')&&(ch !=',')&&(ch !=''))
                {
                    temp[icount]=ch;
                    icount++;
                    fin.get(ch);
                }
                temp[icount]='';
                if(icount>=1)
                {
                    lWord.push_back(Word(temp,paragraphNum,sentenceNum,wordNum));
                }
                if((ch=='.')||(ch=='!')||(ch=='?'))
                {
                    sentenceNum++;
                    wordNum=0;
                }
                if((ch==' ')||(ch==';')||(ch==''))
                {
                    wordNum++;
                }
            }
        }

    如下是我的排序功能,我将单词和它出现的次数组成一个结构体,在第一次打印的时候进行存储。使用了结构体排序方法进行结构体排序,最后输出结构体中数量最高的几个单词,代码展示如下:

    1 typedef struct
    2 {
    3     string danci;//储存单词
    4     int count;//记录单词个数,后面出现几次
    5 } sq;

    排序部分功能如下:

    bool compare(sq a, sq b)
    {
        return (a.count < b.count);
    }
    //调用时的语句
        sort(word_count, word_count + total-1 , compare);
        cout << "total    " << total << " words" << endl;
        for (int i = total; i >total-10; i--)
            cout << left << setw(10) << word_count[i].danci << right << setw(5) << ' ' << word_count[i].count << endl;

    下图展示为《war and peace》部分章节内容统计。具体因为什么内容报错不可排序全部单词目前还不明确。

    总结:

    1三号和四号任务没有完成,其实大部分时间消耗在了学习新知识上面。

    2课余活动少参加,现在已经是研究生了,就少玩一点。

    3对于以前欠下的债,从现在开始补总比不补强太多。

    4会提问才是会学习,我总是没有周围的同学会问,他们总能描述清楚想问什么,而我总是不知道如何张嘴。

    5尽量别累计到一起完成任务,不然太多。

     软件PSP分析

    时间分析:我觉得可以自己可以接受找到代码,然后成功运行(而不是全部重写)这样的情况。所以自己找到的原始代码的好坏,或原始版本的好坏就很大程度上影响了进度。哪怕是参考也会有很大影像。如果使用第一晚的,那可能一直都不能使用大文件,而目前的版本中,排序这部分内容就得新添加。但是目前版本又遇到了不可读参数的问题。我觉得这个软件只要吧词语找到,后续的功能越做越快。具体原因呢,就是有些没有遇到的情况,会花大量的时间去修改。

     代码及版本控制

     git地址:https://git.coding.net/Rio56/wf.git

    (该版本生成的exe可在根目录下读取相应的txt文件。)

    补交作业部分:第二项任务和第三项任务

    第一项任务的完善

    这次是真真实实的从命令台中读取了数据,并且进行词汇统计,消除了大小写的问题。

    代码如下。因为使用了“int _tmain(int argc, _TCHAR* argv[])”所以消耗了更多的时间。其中我突然想到了“wstring”这种数据格式。让我迅速的完成了从_TCHAR*到wchar,wchar到wstring,再从wstring到string的转换。

    感谢高远博同学的帮助,虽然我没使用main(),但是你提示了我很重要的一点:找bug就是找不同。

    wchar的使用是上周就遇到的问题,我因为作业中没有用到就没有记录。而这个没有记录的小知识点正是本次解决问题的起始点。这让我知道:每一次错误的尝试都有被记录的资格! 

    int _tmain(int argc, _TCHAR* argv[])
    {
        //打开某一目录下的txt文件,对其进行读操作
        list<Word> lWord;
        string fileName;
        wstring w_fileName;
     w_fileName = argv[2];
        fileName = WStringToString(w_fileName);
        OpenFile(fileName, lWord);
        Display_for_softwareclass(lWord);
        system("PAUSE");
        return 0;
    }

    第二项任务的截图:

    第三项任务截图:

    测试用例中第一篇文章采用网络上一篇英语作文,实现了读取文件夹下所有txt文件,并统计其中词频的工作。

    如下粘贴部分有关如何读取文件夹中txt文本文件的代码。这部分代码中,我首先获取文件夹内所有文件名称,再获取文件名后四位的字符,与“.txt”作比较,从而获取txt文件。

     1 void getFiles(string path, vector<string>& files)
     2 {
     3     //文件句柄  
     4     long   hFile = 0;
     5     //文件信息  
     6     struct _finddata_t fileinfo;
     7     string p;//定义了一个字符串
     8     char *q;
     9     string last_4;
    10     _finddata_t sFind;
    11     long lResult = 0;
    12     if ((hFile = _findfirst(p.assign(path).append("\*").c_str(), &fileinfo)) != -1)
    13     {
    14         do
    15         {
    16             if ((fileinfo.attrib &  _A_SUBDIR))//_A_SUBDIR(文件夹)如果是文件夹的话
    17             {
    18             //strcmp字符串比较
    19             //.表示当前目录   ..表示当前目录的父目录。
    20             if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0){
    21             getFiles(p.assign(path).append("\").append(fileinfo.name), files);
    22             }
    23             else
    24             {
    25             files.push_back(p.assign(path).append("\").append(fileinfo.name));
    26             }
    27             }
    28             if ((fileinfo.attrib &  _A_SUBDIR))
    29             {
    30                 if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
    31                     getFiles(p.assign(path).append("\").append(fileinfo.name), files);
    32             }
    33             else
    34             {
    35                 last_4 = substring(fileinfo.name, 4);
    36                 if (strcmp(substring(fileinfo.name, 4), ".txt") == 0){
    37                     files.push_back(p.assign(path).append("\").append(fileinfo.name));
    38                 }
    39             }
    40         } while (_findnext(hFile, &fileinfo) == 0);
    41 
    42         _findclose(hFile);
    43     }
    44 }

    如下代码用于截取后四位字符串。

     1 char *substring(char str[], int n) {
     2     char *strT = (char *)malloc(sizeof(char)* (n + 1));
     3 
     4     int len = strlen(str);
     5     int i;
     6     for (int i = 0; i < n; i++) {
     7         strT[i] = str[len - n + i];
     8     }
     9     strT[4] = '';
    10     return strT;
    11 }

    2017-9-26修改:

    1完成了重定向功能!

    2完成了直接命令行输入功能!

    3完成了输入文件名字功能!

    如下展示截图和代码情况。

    此图展示直接粘贴部分段落统计词语截图

    此图展示使用命令行输入“-s war_and_peace.txt”功能,读取文件中内容。(使用我的程序读取文件)

     

    此图展示“重定向功能”通过“<test.txt”命令行读取test.txt文件中的内容。

    如上,所有功能已经全部实现。感谢高远博同学多次对我的指点与教学 。

    发现小的技巧:如果文件路径中包含“空格”会导致程序出现错误。相对路径中存在空格也是不可以的!!!

  • 相关阅读:
    C# winform 获取鼠标点击位置
    C# 读取带有命名空间的xml
    ImageUtility辅助类
    C# 读取XML
    C# 根据生日获取年龄
    C# 将 WebService 封装成动态库
    C# 生成条形码
    C# Ftp Client 基本操作
    C# SQL帮助类
    C# 解压缩文件
  • 原文地址:https://www.cnblogs.com/-Rio56/p/7548942.html
Copyright © 2011-2022 走看看