C语言实现wc.exe

时间:2021-09-18 04:33:41

前言

Github地址:https://github.com/starmiku/xiangmu_wc

编程语言:C

项目简介

wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。

实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。

基本功能列表:

  • wc.exe -c file.c //返回文件 file.c 的字符数 (完成)

  • wc.exe -w file.c //返回文件 file.c 的词的数目 (完成)

  • wc.exe -l file.c //返回文件 file.c 的行数 (完成)

扩展功能:

  • -s 递归处理目录下符合条件的文件未 (未完成)

  • -a 返回更复杂的数据(代码行 / 空行 / 注释行)(完成)

高级功能:

  • -x 这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。 (未完成)

PSP

PSP2.1 预计耗时 实际耗时
总体计划 2h 2.5h
预计完成 10h 12h
程序开发 8h 10h
需求分析 0.5h 1h
设计文档 0.5h 0.5h
设计复审 0.5h 0.5h
代码规范 0.5h 0.5h
具体设计 1h 1h
具体编码 6h 8h
代码复审 1h 1h
程序测试 2h 2h
程序报告 1h 1h
测试报告 1h 0.5h
计算工作量 0.5h 0.5h
事后总结 2.5h 2h
总计时间 12h 14.5h

解题思路

  1. 命令行操作,想到通过C的命令行参数实现。

  2. 行数计算,通过读取'\n'实现。

  3. 字符计算,通过逐一读取字符并排除'\n'和eof实现。

  4. 词数计算,通过读取连续的字母(包括符号.和_)实现。

  5. 空行计算,通过两个换行符之间是否存在字符实现。

  6. 注释行计算,通过读取第一个字符为/进入判断,读取第二个字符为*或者/实现。

  7. 代码行计算,排除空行和注释行之外的即为代码行。

程序流程

程序由主函数和五个功能函数组成。其中test()函数为测试函数,保留作为调试窗口。

C语言实现wc.exe

关键代码

主函数

通过直接读取命令行参数作为程序执行参数,使用while循环提供其他功能的选择。

int main(int argc, char *argv[])
{
    int choose = 0;
    char com; //获取具体命令
    FILE * fp = NULL;
    char* func = (char*)malloc(sizeof(char) * 50); //接收命令参数
    if (argv[2] != NULL && argv[1] != NULL)
    {
        fp = fopen(argv[2], "r");
        func = argv[1];
        com = func[1];
        if (fp == NULL)
        {
            printf("文件打开失败。\n");
            exit(1);
        }
    }
    else
    {
        printf("参数输入错误。\n");
        return 0;
    }
    if (com == 'c')
        choose = 1;
    else if (com == 'w')
        choose = 2;
    else if (com == 'l')
        choose = 3;
    else
        choose = 101;
    while (1)
    {
        switch (choose)
        {
        case 1:
            GetLetter(fp);
            rewind(fp);
            break;
        case 2:
            GetWord(fp);
            rewind(fp);
            break;
        case 3:
            GetLine(fp);
            rewind(fp);
            break;
        case 4:
            MoreData(fp);
            rewind(fp);
            break;
        case 100:
            fclose(fp);
            free(func);
            exit(0);
        case 101:
            printf("错误输入\n\n");
            while (getchar() != '\n') //清空所有输入
                continue;
            break;
        }
        printf("输入:q 退出\t w 获取词数\t l 获取行数\t c 获取字符数\t a 获取更多信息\t");
        scanf("%c", &com);
        getchar();
        if (com == 'c')
            choose = 1;
        else if (com == 'w')
            choose = 2;
        else if (com == 'l')
            choose = 3;
        else if (com == 'a')
            choose = 4;
        else if (com == 'q')
            choose = 100;
        else
            choose = 101;
    }
    //test();
    return 0;
}

功能:获取行数

通过读取'\n'(即ASCII码 10)作为换行标记,如果代码行中出现'\n',fgetc()会分别读取到\和n,不会视作换行。

void GetLine(FILE *f)
{
    int letter = 0, line = 0;
    //接收字符,行数
    int mark = 0;
    while (!feof(f))
    {
        letter = fgetc(f);
        if (letter == '\n')
            line++;
    }
    printf("共有行数 %d\n\n", line);
}

功能:获取词数

如果由连续的字母和'_'则视为词语,如果前后均为符号或空格,单个字母也视作词语。

void GetWord(FILE *f)
{
    int letter = 0, word = 0;
    //接收字符,词数
    int mark = 0;
    //0为非字符,1为字符串
    while (!feof(f))
    {
        letter = fgetc(f);
        if ((letter >= 'a'&&letter <= 'z') || (letter >= 'A'&&letter <= 'Z') || letter == '_')
        {
            if (mark == 0)
                mark = 1;
        }
        else if (letter == '.') //防止头文件和浮点数的'.'被误判
        {
            if (mark == 0 || mark == 1)
                continue;
        }
        else
        {
            if (mark == 1)
            {
                word++;
                mark = 0;
            }
            else
                continue;
        }
    }
    printf("共有词数 %d\n\n", word);
}

功能:获取字符数

将一切能读取的字符均计入字符数,除换行符和eof标志。

void GetLetter(FILE *f)
{
    int letter = 0, num = 0;
    //接收字符,字符数
    while (!feof(f))
    {
        letter = fgetc(f);
        if (letter == '\n') //除换行标记外均视为字符
            continue;
        num++;
    }
    printf("共有字符数 %d\n\n", num - 1);  //除去eof标志
}

拓展:更多数据

先判定是否为空行,如果不为空行则从接收的字符判定为代码行或注释行,通过独立的while循环来读取整行数据,减少判断次数。

void MoreData(FILE *f)
{
    int letter = 0, code = 0, empty = 0, note = 0, mark = 0, notemark = 0;
    /*
    接受字符,代码行数,空行数,注释行数,类型标记,注释行标记
    mark的参数:0空行,1存在格式化字符的空行,2代码行,3注释行
    notemark参数:0未判定,1单行注释,2多行注释
    */
    while (!feof(f))
    {
        letter = fgetc(f);
        if (mark == 0 || mark == 1) //判定空行
        {
            if (letter == ' ')
                continue;
            else if (letter == '\n')
            {
                empty++;
                mark = 0;
                continue;
            }
            else if ((letter == '{' || letter == '}') && mark == 0)
            {
                mark = 1;
                continue;
            }


            else
            {
                if (letter == '/')
                    mark = 3;
                else
                    mark = 2;
            }
        }

        if (mark == 2) //判定代码行
        {
            while (!feof(f) && letter != '\n')
                letter = fgetc(f);
            mark = 0;
            code++;
            continue;
        }

        if (mark == 3) //判定注释行
        {
            while (!feof(f))
            {
                letter = fgetc(f);

                if (letter == '/' && notemark == 0) //单行注释起始判定
                    notemark = 1;
                else if (letter == '*' && notemark == 0) //多行注释起始判定
                    notemark = 2;
                else if (letter == '*' && notemark == 2) //多行注释结束判定
                    notemark = 3;

                if (notemark == 1) //单行注释结束判定
                {
                    while (!feof(f) && letter != '\n')
                        letter = fgetc(f);
                    note++;
                    mark = 0;
                    notemark = 0;
                    break;
                }

                if (notemark == 2)  //多行注释中间行数计算
                {
                    while (!feof(f) && letter != '\n')
                        letter = fgetc(f);
                    note++;
                }

                if (notemark == 3 && letter == '/') //多行注释结束判定
                {
                    while (!feof(f) && letter != '\n')
                        letter = fgetc(f);
                    note++;
                    mark = 0;
                    notemark = 0;
                    break;
                }
                else if (notemark == 3 && letter != '*') //判定是否为注释中的*
                    notemark = 2;
            }
        }
    }
    printf("共有空行数 %d  代码行数 %d  注释行数 %d\n", empty, code - 1, note); //除去eof行
}

测试运行

测试代码

#include<stdio.h>

int main()
{
}

//test

/*
testfile
*/

测试截图

C语言实现wc.exe