一、Github项目地址

  https://github.com/AllForward/GP_Homework/tree/master/个人项目


二、题目叙述

  这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。

  1、具体功能要求:
    程序处理用户需求的模式为:wc.exe [parameter] [file_name]

  2、基本功能列表:

  1. wc.exe -c file.c     //返回文件 file.c 的字符数
  2. wc.exe -w file.c    //返回文件 file.c 的词的数目
  3. wc.exe -l file.c      //返回文件 file.c 的行数

  3、扩展功能:

  1. -s   递归处理目录下符合条件的文件。
  2. -a   返回更复杂的数据(代码行 / 空行 / 注释行)。

  4、高级功能:

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

  5、注意事项:  

  1. 空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。
  2. 代码行:本行包括多于一个字符的代码。
  3. 注释行:本行不是代码行,并且本行包括注释。(注意:} //注释 算作是注释行)
  4. [file_name]: 文件或目录名,可以处理一般通配符。(一般通配符包括[],*,?,!,^)

三、解题思路

  1、首先题目要求说要对文本的内容进行一些统计,很显然需要采用IO流来对文件进行读取,之后对读取的内容进行逐一统计,由于本人对Java语言较为熟悉,所以决定采用Java来进行编程;

    2、对于文件内容的统计,Java有对应的API可以读取每一行的内容,那么我就对每次读到的一行的内容进行分析统计,具体分析统计过程如下:每次将读取的一行内容用字符串类型进行存储;

  • 对于字符数,我先将每一行内容中的空格去掉,之后再获取字符串的长度,这样就可以得到每一行的字符数,最后将每一行的字符数相加就为文件总的字符数;
  • 对于词的数量,则是采用正则表达式的方式将每一行读取到的内容,通过空格、分号、逗号、括号、点等标点符号进行分割成多个字符串,再通过正则将这多个字符串中非字母的字符去除,最终获得的字符串若不为空就是一个单词;(例子:读取到的行内容为:
    package com.company.FileStatistics; 那么经过分割后的多个字符串分别为:package,com,company,FileStatistics。由于这些字符串中的不存在非字母的字符,所以最终结果就是这4个单词;)最后将每一行的单词数相加就为文件总的单词数;
  • 对于文件行数,那么就是通过循环读取文件的每一行,每读一行总行数+1,直到读完整个文件;
  • 对于递归处理目录下符合条件的文件,则是通过文件名通配符对当前路径下的所有文件进行扫描,一旦有符合的文件名,就将该文件加入到文件数组中,最后通过循环对文件数组的中每一个文件进行统计分析;
  • 对于更复杂的数据(代码行 / 空行 / 注释行),首先是空行,那么每当读取到的内容为空,或者改行只有一个字符,例如{,},那就是空行;注释行则是要有/**/,//,我通过正则表达式的方式来判断该行是否为注释行,其中需要注意到/**/形式的注释可以跨越多行,以及类似于"{ //"的注释行;代码行的话,则是需要该行在除去注释内容的基础上,剩余的字符数多于1个;
  • 最后是GUI界面,我是通过Java的图形库JavaJW来实现的,主要是通过设置一个文件选择框,来让用户选择文件,之后返回文件的统计结果;

      


四、设计实现过程

 整个程序分成三个类:

  主类:Main,普通及扩展功能HandleFile类以及GUI图形界面类Swing

 整个程序分成几个主要函数:

  1. FileHandle(String[] args)接收控制台输入,同时判断是否要进行GUI界面来统计,不是则调用ispermit函数判断是否为可以进行统计的文件类型,是则调用change(String matches)对用户输入的文本通配符转换成java正则表达式,再调用getFiles()函数获取符合条件的文件,是单个还是多个;
  2. FileStatistics(File file, String type) 获取用户想要统计的类型,是空行还是单词数等等;
  3. SwingStatistics()通过GUI图形界面进行统计的函数等等统计函数;

  流程图如下:

    


五、代码说明

1.Main类

package com.company;

import com.company.FileStatistics.HandleFile;

public class Main {

    public static void main(String[] args) {

        HandleFile.FileHandle(args);
}
}

2. HandleFile类

package com.company.FileStatistics;

import java.io.*;
import java.util.ArrayList;
import java.util.List; /**
* @description 文件基础以及扩展功能实现
* @author guopei
* @date 2020-03-13 10:55
*/ public class HandleFile { //当前exe所在的文件路径
public static final String address = System.getProperty("exe.path"); public static Boolean isNotes = false; public static void FileHandle(String[] args) { // System.out.println("当前路径为:" + address);
if (args[0].equals("-x")) {
Swing.SwingStatistics();
return;
}
List<File> fileList = new ArrayList<File>();
//对发送过来的命令进行判断
if (isPermit(args[args.length - 1])) {
File file = new File(address);
args[args.length - 1] = change(args[args.length - 1]);
if (file.isDirectory()) {
//如果是文件夹,获取当下的符合条件的文件
getFiles(fileList, file.getPath(), args[args.length - 1]);
}
else {
if (file.getName().matches(args[args.length - 1])) {
fileList.add(file);
}
}
//命令是递归查询多个文件
if (args[0].equals("-s")) {
if (fileList.size() < 1) {
System.out.println("查找不到符合条件的文件");
return;
}
//说明要统计多个文件
for (File f : fileList) {
String response = FileStatistics(f, args[1]);
if ("不支持的命令格式".equals(response)) {
System.out.println(response);
break;
}
else if (response != null) {
System.out.println(response);
}
}
}
//命令是查询单个文件
else {
if (fileList.size() < 1) {
System.out.println("查找不到符合条件的文件");
return;
}
else if (fileList.size() > 1) {
System.out.println("查找到不止一个符合条件的文件,请加入-s命令来递归处理多个文件");
return;
}
System.out.println(FileStatistics(fileList.get(0), args[0]));
}
}
else {
System.out.println("文件格式不支持");
}
} private static void getFiles(List<File> fileList, String path, String matches) {
File file = new File(path);
File[] files = file.listFiles();
for(File fileIndex:files){
//如果这个文件是目录,则进行递归搜索
if(fileIndex.isDirectory()){
getFiles(fileList, fileIndex.getPath(), matches);
}else {
//如果文件是符合匹配条件的文件,则将文件放入集合中
if (fileIndex.getName().matches(matches)) {
fileList.add(fileIndex);
}
}
}
} private static Integer WordStatistics(String content) {
//统计词的数目
String copy = content;
Integer num = 0;
//统计词的个数,将每一行数据切割成多个子字符串
String[] strings = copy.split("[\\s+,\\.\n\\;\\(\\<\\[\\{]");
//对切割后的每个字符串进行判断
for (int i = 0; i < strings.length; i++) {
//将字符不是字母的去除
strings[i] = strings[i].replaceAll("[^a-zA-Z]", "");
if (!strings[i].equals("")) {
//如果不是空行,单词数+1
num++;
}
}
return num;
} //对文本通配符转换成java的正则表达式
public static String change(String matches) {
String[] split = matches.split("\\.");
matches = matches.replace(".", "");
matches = matches.replace(split[split.length - 1], "(." + split[split.length - 1] + ")");
matches = matches.replaceAll("\\?", ".{1}");
matches = matches.replaceAll("\\*", ".+");
matches = matches.replaceAll("!", "^");
return matches;
} //统计更复杂的数据(代码行 / 空行 / 注释行)
public static Integer[] LineStatistics(String content) {
//顺序为:空行 / 注释行
Integer[] lines = {0, 0, 0}; //统计空行数
if (content.equals("") || content.matches("[\\{\\}]")) {
lines[0]++;
}
//统计注释行
else if (content.matches("(.*)\\/\\/(.*)")) {
lines[1]++;
}
else if (content.matches(".*\\/\\*.*\\*\\/")) {
//说明使用了/**/的注释形式
lines[1]++;
}
else if (content.matches("(.*)\\/\\*(.*)")) {
//说明使用了/*的注释形式
lines[1]++;
isNotes = true;
}
else if (content.matches("(.*)\\*\\/(.*)")) {
lines[1]++;
isNotes = false;
}
else if (isNotes == true) {
lines[1]++;
}
//统计代码行
if (content.matches(".{2,}") && isNotes == false && content.matches("(.*)[^\\\\/\\\\*](.*)")) {
//去除掉注释部分任然含有两个及以上字符则就是代码行
String copy = content;
copy = copy.replaceAll("\\/\\*.*\\*\\/", "");
copy = copy.replaceAll("\\/\\/(.*)", "").trim();
if (copy.matches(".{2,}")) {
lines[2]++;
}
}
return lines;
} //正则判断文件格式是否支持进行统计(符合的标准为.c,.java,.cpp,.py,.txt)
private static Boolean isPermit(String fileType) {
if (fileType.matches(".+(.c|.java|.cpp|.py|.txt)$")) {
return true;
}
return false;
} /**
* wc.exe -c file.c //返回文件 file.c 的字符数
*
* wc.exe -w file.c //返回文件 file.c 的词的数目
*
* wc.exe -l file.c //返回文件 file.c 的行数
* 扩展功能:
* -s 递归处理目录下符合条件的文件。
* -a 返回更复杂的数据(代码行 / 空行 / 注释行)。
* @param file
* @param type
* @return
*/
public static String FileStatistics(File file, String type) {
//行数
Integer line = 0; //单词数
Integer wordNum = 0; //字符数
Integer charNum = 0; //空行数
Integer nullLine = 0; //注释行
Integer notesLine = 0; //代码行
Integer codeLine = 0; try {
//判断文件类型是否符合
if (!isPermit(file.getName())) {
System.out.println("不支持的文件格式");
return "不支持的文件格式";
}
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
String content = null;
Boolean isNotes = false;
while ((content = bufferedReader.readLine()) != null) {
//去除字符串的首尾空格
content = content.trim();
//System.out.println(content);
switch (type) {
case "-c":
//返回文件的字符数
//将空格全部去除
content = content.replaceAll(" +", "");
charNum += content.length();
break;
case "-w":
//统计词的数目
wordNum += WordStatistics(content);
break;
case "-a":
Integer[] lines = LineStatistics(content);
nullLine += lines[0];
notesLine += lines[1];
codeLine += lines[2];
break;
case "-x":
//统计全部信息
content = content.replaceAll(" +", "");
charNum += content.length();
wordNum += WordStatistics(content);
Integer[] Lines = LineStatistics(content);
nullLine += Lines[0];
notesLine += Lines[1];
codeLine += Lines[2];
break;
case "-l":
break;
default:
return "不支持的命令格式";
}
//总行数
line++;
}
switch (type) {
case "-c":
return file.getName() + "文件总字符数:" + charNum;
case "-w":
return file.getName() + "文件的单词数:" + wordNum;
case "-l":
return file.getName() + "文件总行数:" + line;
case "-a":
return file.getName() + ":\r\n" +
"文件空行数:" + nullLine + "\r\n" +
"文件注释行数:" + notesLine + "\r\n" +
"代码行数:" + codeLine;
case "-x":
return file.getName() + ":\r\n" +
"文件总字符数:" + charNum + "\r\n" +
"文件的单词数:" + wordNum + "\r\n" +
"文件总行数:" + line + "\r\n" +
"文件空行数:" + nullLine + "\r\n" +
"文件注释行数:" + notesLine + "\r\n" +
"代码行数:" + codeLine;
default:
return "不支持的命令格式";
}
} catch (FileNotFoundException e) {
System.out.println("该文件或文件名不存在");
} catch (IOException e) {
System.out.println("文件读取错误");
}
return null;
}
}

3.Swing类

package com.company.FileStatistics;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File; /**
* @description 图形界面统计
* @author guopei
* @date 2020-03-15 17:50
*/
public class Swing { public static void SwingStatistics() { JFrame frame = new JFrame("文件选择");
frame.setLayout(new FlowLayout());
JFileChooser chooser = new JFileChooser();
JButton bOpen = new JButton("打开文件");
frame.add(bOpen);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(250, 150);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
bOpen.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int returnVal = chooser.showOpenDialog(frame);
File file = chooser.getSelectedFile();
if (returnVal == JFileChooser.APPROVE_OPTION) {
JOptionPane.showMessageDialog(frame, "计划打开文件:" + file.getAbsolutePath());
JOptionPane.showMessageDialog(frame, "文本内容统计:\n" + HandleFile.FileStatistics(file, "-x"));
}
}
});
}
}

六、测试运行

测试文件:

1.cpp(空文件) 

2.cpp(内容如下图所示)

测试效果:

1.统计空文件1.cpp效果图:(其中包括-w,-c以及-l命令)

2.统计非空文件2.cpp效果图:(其中包括-a,-w,-c以及-l命令)

3.统计多个文件效果图:(其中包括-a命令)

匹配所有.cpp文件

匹配?.cpp文件

匹配非1.cpp的文件

4. GUI界面统计效果图:

输入FileStatistics.exe -x后按回车便会有GUI图形界面提示

点击"打开文件"按钮后的效果如下:

从其中选择一个符合统计条件的文件进行统计:

若选择了不能统计的文件,则会有相应的提示,效果如下:(例如选择了一个exe文件)


七、PSP表格

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

15

20

· Estimate

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

180

200

Development

开发

130

140

· Analysis

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

10

10

· Design Spec

· 生成设计文档

50

60

· Design Review

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

10

10

· Coding Standard

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

8

10

· Design

· 具体设计

20

25

· Coding

· 具体编码

130

140

· Code Review

· 代码复审

20

25

· Test

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

30

30

Reporting

报告

10

10

· Test Report

· 测试报告

10

10

· Size Measurement

· 计算工作量

10

20

· Postmortem & Process Improvement Plan

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

10

10

合计

250

260


八、项目小结
  本次项目让我对于Java的IO流以及正则表达式的相关知识有了进一步地巩固,同时通过PSP表格可以发现,个人实际的开发时间是比预计的时间长的,主要的原因是在一些知识点上还不够熟悉,需要通过查阅相关资料以及笔记才能得以解决,同时在编写代码的时候.,有一些地方没有注意,导致在测试的时候出现bug,因此返回去修改bug消耗的时间也是比较长的.总的来说就是分析问题需要再细致一些,对于一些比较特殊的情况要进行综合考虑分析,还有就是知识点需要不断地复习!

对文本通配符转换成java的正则表达式

个人项目(Word Count)的更多相关文章

  1. 软件工程第三个程序:“WC项目” —— 文件信息统计(Word Count ) 命令行程序

    软件工程第三个程序:“WC项目” —— 文件信息统计(Word Count ) 命令行程序 格式:wc.exe [parameter][filename] 在[parameter]中,用户通过输入参数 ...

  2. 个人项目作业-Word Count

    个人项目作业 1.Github地址 https://github.com/CLSgGhost/SE_work 2.项目相关需求 wc.exe 是一个常见的工具,它能统计文本文件的字符数.单词数和行数. ...

  3. mac上eclipse上运行word count

    1.打开eclipse之后,建立wordcount项目 package wordcount; import java.io.IOException; import java.util.StringTo ...

  4. Word Count作业

    Word Count作业 一.个人Gitee地址:https://gitee.com/Changyu-Guo 二.项目简介 该项目主要是模拟Linux上面的wc命令,基本要求如下: 命令格式: wc. ...

  5. Word Count

    Word Count 一.个人Gitee地址:https://gitee.com/godcoder979/(该项目完整代码在这里) 二.项目简介: 该项目是一个统计文件字符.单词.行数等数目的应用程序 ...

  6. Java --本地提交MapReduce作业至集群☞实现 Word Count

    还是那句话,看别人写的的总是觉得心累,代码一贴,一打包,扔到Hadoop上跑一遍就完事了????写个测试样例程序(MapReduce中的Hello World)还要这么麻烦!!!?,还本地打Jar包, ...

  7. Word Count(C语言)

    1.项目地址 https://github.com/namoyuwen/word-count 2.项目相关要求 2.1 项目描述 Word Count    1. 实现一个简单而完整的软件工具(源程序 ...

  8. Spark: 单词计数(Word Count)的MapReduce实现(Java/Python)

    1 导引 我们在博客<Hadoop: 单词计数(Word Count)的MapReduce实现 >中学习了如何用Hadoop-MapReduce实现单词计数,现在我们来看如何用Spark来 ...

  9. Hive Word count

    --https://github.com/slimandslam/pig-hive-wordcount/blob/master/wordcount.hql DROP TABLE myinput; DR ...

随机推荐

  1. 在腾讯云centos7.2上安装配置Node.js记录

    应为爱好前端所以打算在腾讯云服务器上安装JavaScript引擎Node.js,下面是安装步骤: 安装准备: 下载node.js的.tar.xz安装包:https://nodejs.org/dist/ ...

  2. JQuery之选择器篇(一)

      今天回顾了之前学习的JQuery选择器,现在简单的总结一下. JQuery选择器类型   主要分为四类 基本选择器 层级选择器 过滤选择器 表单选择器 基本选择器   基本选择器是jQuery中最 ...

  3. 当鼠标hover的时候,使用tip将overflow:hidden隐藏的文字显示完全

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  4. LeetCode#476 Number Complement - in Swift

    Given a positive integer, output its complement number. The complement strategy is to flip the bits ...

  5. LeetCode--二叉树2--运用递归解决树的问题

    LeetCode--二叉树2--运用递归解决树的问题 在前面的章节中,我们已经介绍了如何利用递归求解树的遍历. 递归是解决树的相关问题最有效和最常用的方法之一. 我们知道,树可以以递归的方式定义为一个 ...

  6. nginx能访问html静态文件但无法访问php文件

    nginx.conf中红框部分修改成你的实际网站根目录

  7. 使用veloticy-ui生成文字动画

    前言 最近要实现一个类似文字波浪线的效果,使用了velocity-ui这个动画库,第一个感觉就是使用简单,代码量少,性能优异,在此简单介绍一下使用方法,并实现一个看上去不错的动画.具体使用方法可以点击 ...

  8. JS对象之封装(二)

    JS 对象封装的常用方式 1.常规封装 function Person (name,age){ this.name = name; this.age = age; } Pserson.prototyp ...

  9. 2020年,大厂常问iOS面试题汇总!

    Runloop & KVO runloop app如何接收到触摸事件的 为什么只有主线程的runloop是开启的 为什么只在主线程刷新UI PerformSelector和runloop的关系 ...

  10. SpringBoot整合Mybatis对单表的增、删、改、查操作

    一.目标 SpringBoot整合Mybatis对单表的增.删.改.查操作 二.开发工具及项目环境 IDE: IntelliJ IDEA 2019.3 SQL:Navicat for MySQL 三. ...