• 软件工程实践-WC项目之C实现


    1、Github项目地址

    https://github.com/ShadowEvan/homework

    • 基本功能
      • -c 统计文件字符数(实现)
      • -w 统计文件词数(实现)
      • -l  统计文件行数(实现)
    • 扩展功能
      • -s 递归处理目录下符合条件的文件(实现)
      • -a 返回文件代码行/空行/注释行(实现)
      • 简单处理一般通配符(*, ?)(实现)
    • 高级功能
      • -x 支持图形界面(未实现)

    2、PSP表格

    PSP2.1

    Personal Software Process Stages

    预估耗时

    (分钟)

    实际耗时

    (分钟)

    Planning

    计划

     20  30

    · Estimate

    · 估计这个任务需要多少时间

     1900  2340

    Development

    开发

    600  1200

    · Analysis

    · 需求分析 (包括学习新技术)

     90  120

    · Design Spec

    · 生成设计文档

     30  45

    · Design Review

    · 设计复审 (和同事审核设计文档)

     30  45

    · Coding Standard

    · 代码规范 (为目前的开发制定合适的规范)

     20  20

    · Design

    · 具体设计

     40  60

    · Coding

    · 具体编码

     240  300

    · Code Review

    · 代码复审

     120  180

    · Test

    · 测试(自我测试,修改代码,提交修改)

     120  180

    Reporting

    报告

     60  100

    · Test Report

    · 测试报告

     30  30

    · Size Measurement

    · 计算工作量

     20  30

    · Postmortem & Process Improvement Plan

    · 事后总结, 并提出过程改进计划

     30  30

    合计

       1920 2370 

    3、解题思路

      拿到题目后,觉得基本功能比较容易实现,扩展功能递归遍历文件有点困难,需要查阅相关资料。我的方法主要是,打开文件后每次获取一个字符并进行处理。

    (1)返回文件的字符数  

    思路:每获取一个非空字符及非转义字符如 、 则进行统计。

    (2)返回文件的词数  

    思路:每遇到一个字母或数字则标记为进入词内状态并进行统计,词内状态下遇到字母或数字不统计,遇到其他字符则标记为词外状态。

    (3)返回文件的行数

    思路:行数上仅记录有效行数,即不包括文本或代码的空行不计在内,方法类似于词数统计。

    (4)递归处理目录下符合条件的文件

    思路:先识别文件名是否含有通配符,要求输入的第一个参数为-s,满足条件则开始获取程序当前路径,并递归遍历查找与模式匹配的文件名,按其他参数处理符合条件的文件。

    这个功能应该是耗时最长完成的,主要原因在于对于一些文件处理的库和函数不够熟悉,需要花大量时间在网上查找并筛选合适的资料。在这方面有个缺陷就是,所查找到的库是有系统依赖性的,这意味着无法做到跨平台,因此还有许多改进空间。

    (5)返回更复杂的数据

    思路:方法大体与(2)和(3)一致,利用状态来统计,主要的难点在于多行注释以及各种边界情况,为此我另外设置一个函数来处理多行注释的问题。以下是我在这个功能中对一些概念的界定:

    若注释前本行有代码,本行算代码行。

    若本行仅有 { 或 } ,本行算空行。

    若多行注释中包括空行,该空行算注释行。


    4、设计实现过程

    int option(int argc, char *argv[], char *file_name);
    int recurrence(int argc, char *argv[], char *file_name);
    int file_travesal(const char *dir, int argc, char *argv[], char *file_name);
    int wildchar_match(char *file_name, char *pattern, int ignore_case);
    unsigned int char_count(FILE *fp);
    unsigned int word_count(FILE *fp);
    unsigned int line_count(FILE *fp);
    void code_count(FILE *fp);
    函数option 中包含char_count(-c)、word_count(-w)、line_count(-l)、code_count(-a)
    函数recurrence封装了file_travesal函数,file_travesal中运用了wildchar_match函数和option函数

    5、代码说明
     1 unsigned int char_count(FILE *fp)
     2 {
     3     unsigned int cc = 0;
     4     char ch;
     5 
     6     while ((ch = getc(fp)) != EOF) {
     7         if (ch != ' ' && ch != '
    ' && ch != '	') {
     8             cc++;
     9         }
    10     }
    11 
    12     return cc;
    13 }
    字符统计
     1 unsigned int word_count(FILE *fp)
     2 {
     3     unsigned int wc = 0;
     4     char ch;
     5     bool state = _OUT;
     6 
     7     while ((ch = getc(fp)) != EOF) {
     8         if ((ch > 'z' || ch < 'a') && (ch > 'Z' || ch < 'A') && (ch > '9' || ch < '0')) {
     9             if (state == _IN) {
    10                 state = _OUT;
    11             }
    12         } else if (state == _OUT) {
    13             state = _IN;
    14             wc++;
    15         }
    16     }
    17 
    18     return wc;
    19 }
    词数统计
     1 unsigned int line_count(FILE *fp)
     2 {
     3     unsigned int lc = 0;
     4     char ch;
     5     bool state = _OUT;
     6 
     7     while ((ch = getc(fp)) != EOF) {
     8         if (ch != ' ' && ch != '	' && ch != '
    ' && state == _OUT) {
     9             state = _IN;
    10             lc++;
    11         } else if ('
    ' == ch) {
    12             state = _OUT;
    13         }
    14     }
    15 
    16     return lc;
    17 }
    行数统计
     1 void code_count(FILE *fp)
     2 {
     3     unsigned int n_code = 0;
     4     unsigned int n_blank = 0;
     5     unsigned int n_comment = 0;
     6     char ch1, ch2;
     7     bool in_code = false;
     8     bool in_comment = false;
     9 
    10     while ((ch1 = getc(fp)) != EOF) {
    11         if (ch1 != ' ' && ch1 != '	' && ch1 != '
    ' && ch1 != '{' && ch1 != '}' && !in_comment && !in_code) {
    12             if (ch1 == '/') {
    13                 // Multi-line comment
    14                 if ((ch2 = getc(fp)) == '*') {
    15                     n_comment = in_multicomment(fp, n_comment);
    16                 } else if (ch2 == '/') {
    17                     in_comment = true;
    18                     n_comment++;
    19                 } else {
    20                     in_code = true;
    21                     n_code++;
    22                 }
    23             } else {
    24                 in_code = true;
    25                 n_code++;
    26             }
    27         // Reset the state and count blank lines for each line
    28         } else if ('
    ' == ch1) {
    29             if (in_code) {
    30                 in_code = false;
    31             } else if (in_comment) {
    32                 in_comment = false;
    33             } else
    34                 n_blank++;
    35         }
    36     }
    37 
    38     printf("Blank lines: %d
    ", n_blank);
    39     printf("Code lines: %d
    ", n_code);
    40     printf("Comment lines: %d
    ", n_comment);
    41 }

    根据遇到的字符设置状态,/*则为多行注释,//为单行注释,其余非空行为代码行

    每处理完一行则重置状态并统计空行

     1 /* Get current work directory and traverse recursively the directory */
     2 int recurrence(int argc, char *argv[], char *file_name)
     3 {
     4     char *buffer;
     5 
     6     if ((buffer = _getcwd(NULL, 0)) == NULL) {
     7         perror("_getcwd error");
     8         free(buffer);
     9         exit(-1);
    10     }
    11 
    12     file_travesal(buffer, argc, argv, file_name);
    13     free(buffer);
    14 
    15     return 0;
    16 }
    17 
    18 int file_travesal(const char *dir, int argc, char *argv[], char *file_name)
    19 {
    20     struct _finddata_t fa;
    21     long handle;
    22     char new_dir[MAX_PATH];
    23     strcpy(new_dir, dir);
    24     strcat(new_dir, "\*.*");
    25 
    26     if ((handle = _findfirst(new_dir, &fa)) == -1L) {
    27         printf("Failed to find first file!
    ");
    28         return -1;
    29     }
    30 
    31     do {
    32         // Determine if it is a subdirectory
    33         if (fa.attrib & _A_SUBDIR) {
    34             if ((strcmp(fa.name, ".") != 0) && (strcmp(fa.name, "..") != 0)) {
    35                 strcpy(new_dir, dir);
    36                 strcat(new_dir, "\");
    37                 strcat(new_dir, fa.name);
    38                 file_travesal(new_dir, argc, argv, file_name);
    39             }
    40         } else {
    41             // Skip it when not match
    42             if (!wildchar_match(fa.name, file_name, 1)) {
    43                 continue;
    44             }
    45             strcpy(new_dir, dir);
    46             strcat(new_dir, "\");
    47             strcat(new_dir, fa.name);
    48             printf("%s:
    ", fa.name);
    49             option(argc, (argv + 1), new_dir);
    50             printf("
    ");
    51         }
    52     } while (_findnext(handle, &fa) == 0);
    53     return 0;
    54 }

    获取程序当前工作目录,并递归遍历整个目录文件,若文件名与用户输入的模式匹配,则执行选项操作

     1 int option(int argc, char *argv[], char *file_name)
     2 {
     3     int c = 0;
     4     FILE *fp;
     5 
     6     if ((fp = fopen(file_name, "r")) == NULL) {
     7         printf("Error! There has been a problem opening or reading from the file.
    ");
     8         exit(-1);
     9     }
    10 
    11     if (argc == 2) {
    12         printf("The number of characters: %d
    ", char_count(fp));
    13         rewind(fp);
    14         printf("The number of words: %d
    ", word_count(fp));
    15         rewind(fp);
    16         printf("The number of lines: %d
    ", line_count(fp));
    17     }
    18     
    19     // Get the arguments and execute the relevant operation
    20     while (--argc > 0 && (*++argv)[0] == '-') {
    21         while (c = *++argv[0]) {
    22             switch (c) {
    23                 case 'c':
    24                     printf("The number of characters in the file is %d
    ", char_count(fp));
    25                     rewind(fp);
    26                     break;
    27                 case 'w':
    28                     printf("The number of words in the file is %d
    ", word_count(fp));
    29                     rewind(fp);
    30                     break;
    31                 case 'l':
    32                     printf("The number of lines in the file is %d
    ", line_count(fp));
    33                     rewind(fp);
    34                     break;
    35                 case 'a':
    36                     code_count(fp);
    37                     rewind(fp);
    38                     break;
    39                 default:
    40                     printf("Illegal option -%c
    ", c);
    41                     break;
    42             }
    43         }
    44 
    45         // recover argv for next use.
    46         --argv[0];
    47         --argv[0];
    48     }
    49     
    50     fclose(fp);
    51     return 0;
    52 }

    遍历所有参数,若参数的第一个字符为‘-’,则把其第二个字符赋值给c,并用switch结构进行选项操作。

    为实现多选项执行,每完成一个选项将文件指针指回头部。

    46-47行恢复参数,防止指针指向的内容发生变化并对文件遍历造成影响。


    6、测试运行
      1、空文件

      2、只有一个字符的文件

      3、只有一个词的文件

      4、只有一行的文件

      5、一个典型的源文件

      6、扩展功能


    7、实际花费时间(见开头表格)


    8、项目小结
    1. 在这个项目中,首先是对编程基本功的巩固,这次实践能使我更熟练地应用编程语言开发功能。
    2. 更深刻地体会到模块化设计开发的好处,虽然自己仍有待提高,但在开发过程中采用这种方式可以大大减少高耦合出现的情况。
    3. 了解并学习了目录文件的处理,查阅资料的过程中学习到了很多新的知识。
    4. PSP表格有助于开发者正确地认识自己,此次开发我耗时过长,藉此发现了自己的许多不足之处,比如对于不熟悉的领域一方面不敢大胆尝试,另一方面又颇有点闭门造车,耽误了大量时间和精力。
    5. 程序上,对于各种特殊情况的处理和测试仍不够完善,还需要多多努力。
  • 相关阅读:
    Part0:安装Django
    计算机技术与科学系列笔记
    svg基础知识体系建立
    js如何判断字符串里面是否含有某个字符串
    js字符串如何倒序
    js判断值是否是数字
    HTML DOM 知识点整理(一)—— Document对象
    Git hub pull时候的错误 : The current branch is not configured for pull No value for key branch.master.merge found in configuration
    Map的3种遍历[轉]
    如何刪除GitHub中的repository
  • 原文地址:https://www.cnblogs.com/lingyunshangyue/p/9649736.html
Copyright © 2020-2023  润新知