统计.java源文件的关键字

问题叙述&分析

问题叙述:

实现一个类 Keyordldentifer,其中的核心方法的功能是读入1个 java 程序源文件,在控制台输出各个关键字的个数。

  • 对Java语言的所有关键字进行统计,不是仅针对部分关键字。
  • 要求具备“跳过注释”功能,注释中出现的关键字不计入关键字个数(要求支持单行注释“/... 和多行注释“/*/”的跳过)。

问题分析:

对java代码的关键字的统计可分为以下流程:

1.读取文件

题中读取的是java程序源文件——文本文件,因此可以使用 BufferedReader 字符流来读取 java 程序源文件。同时使用 BufferedReader 的好处在于,BufferedReader 提供了逐行读取文本的方法,对于本题中 java 代码的读取和文本分析更为方便。

//1.造流
br = new BufferedReader(new FileReader(inFile));
//2.数据操作
String data;
while ((data = br.readLine()) != null) {
    //对字符串data进行一系列处理操作
}

2.对字符串data进行处理

因为要求统计的是 java 关键字,所以首先将53个关键字记录在 keywords.txt 文本文件中,在程序刚开始时,将此文件加载到内存中为一个 List< String > javaWords。然后对 data 中的每个单词进行检查,若javaWords中有,则加入统计。

if (javaWords.contains(word)) {
    wordsCount.put(word, wordsCount.getOrDefault(word, 0) + 1);
    return 1;
}

同时对于例如 "Map<String, Integer>" 这种字符串,需要将不符合命名规则的字符替换为空格字符,在进行关键字统计。对于 ‘/’ 和 ‘*’ 字符,应忽略,放在后面进行进一步处理。

StringBuilder sb = new StringBuilder(data);
for (int i = 0; i < sb.length(); i++) {
    char c = data.charAt(i);
    if (!Character.isLetterOrDigit(c) && c!='_' && c != '/' && c != '*') {
        sb.replace(i,i+1," ");
    }
}
stringHandle(sb.toString());

3.处理字符串中的 // 和 /**/

若遇到 //,则将 // 后的所有内容抛弃掉,只取 // 之前的字串;若遇到 /* , 则将 /* 后的所有内容抛弃掉,只取 /* 之前的子串,同时此时设置一个标志 flag=1,后续只要 flag 为1,则内容全部抛弃,直到遇到 */,令 flag=0,并取 */ 之后的子串。

String[] words = str.split(" ");
for (String word : words) {
    if (word == null) {
        continue;
    } else if (word.contains("//")) {
        int index = word.indexOf("//");
        String substring = word.substring(0, index);
        wordCount(substring);
        break;
    } else if (word.contains("/*")) {
        int index = word.indexOf("/*");
        String substring = word.substring(0, index);
        wordCount(substring);
        flag = 1;
        break;
    } else if (flag == 0) {
        wordCount(word);
    } else if (flag == 1 && word.contains("*/")) {
        int index = word.indexOf("*/");
        String substring = word.substring(index+2);
        wordCount(substring);
        flag = 0;
    }
}

4.输出统计结果

因为是使用 Map 进行统计关键字,所以只需要将统计关键字的 Map<String, Integer> wordsCount 转化为 entrySet 遍历输出即可。这里可以使用 java8 的新特性方法引用,是遍历输出的一个小技巧。

wordsCount.entrySet().forEach(System.out::println);

测试代码

KeywordIdentifierTest.java

@Test
public void keywordsCount() {
    identifier.keywordsCount();
}

运行结果

在终端显示:

java根据名称查找文件路径 java查找文件内关键字_Java

存储到.properties文件中

java根据名称查找文件路径 java查找文件内关键字_java根据名称查找文件路径_02

一些问题

  1. 程序中涉及到数次对文件输入流输出流的关闭操作,有冗余代码重复的嫌疑,因此创建一个 FileUtils 工具类,设置4个重载的 public static void closeStream() 方法,通过传入的流的不同,调用相应的关闭流的方法。
  2. 在将字符串中会干扰统计关键字的字符替换为空格字符时,涉及到大量的字符串的修改操作,使用String进行修改,因为每次修改都会返回一个新的 String,再加上本题不涉及线程安全问题,因此这里使用 StringBuilder 来操作。
  3. 统计完关键字的数量后,鉴于已经涉及了很多文件的 io 操作,那为什么不也将统计的结果保存起来呢?统计关键字用的是 Map,因此正好可以很方便地保存在 .properties 文件中
for (Map.Entry<String, Integer> entry : wordsCount.entrySet()) {
    properties.setProperty(entry.getKey(), entry.getValue().toString());
}