diff --git a/221801427/README.md b/221801427/README.md
new file mode 100644
index 00000000..6db2a0ad
--- /dev/null
+++ b/221801427/README.md
@@ -0,0 +1,80 @@
+# PersonalProject-Java
+
+
+----------
+
+
+## 功能简介
+输入英文文本(只考虑Ascii码,汉字不需考虑)
+
1. 统计字符数(空格,水平制表符,换行符,均算字符)
+
2. 统计有效行数(任何包含非空白字符的行,都需要统计)
+
3. 统计单词数(至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写)
+
4. 统计频率最高的10个单词的出现次数(值降序,键字典序)
+
按以上顺序逐行打印至输出文本,输出的格式如下
+
+> characters: number
+words: number
+lines: number
+word1: number
+word2: number
+...
+
+
+----------
+
+
+## 运行方式
+运行cmd
+
+ javac -encoding UTF-8 WordCount.java
+ java WordCount input.txt output.txt
+
+
+----------
+
+
+## 作业链接
+[寒假作业(2/2) 作业要求](https://edu.cnblogs.com/campus/fzu/2021SpringSoftwareEngineeringPractice/homework/11740)
+
+
+----------
+
+
+## 博客链接
+[我的博客](https://www.cnblogs.com/railgunSE/)
+
+
+----------
+
+
+## 签入记录
+### 第一次commit
+建立了基本架构,制定了代码规范
+### 第二次commit
+实现字符计算,以及对应的文件读写功能
+### 第三次commit
+重写了算法实现,增加计算行数功能
+### 第四次commit
+增加了计算有效单词总数的功能
+### 第五次commit
+增加了统计词频功能,输出功能也完整了
+### 第六次commit
+改用StringBuilder提高性能,修改、增加了一些代码规范上的细节
+### 第七次commit
+通过去删去不必要的字符操作,改进了部分算法效率
+### 第八次commit
+修改了读取算法与计算行数算法以适应读取\r的需要
+### 第九次commit
+输出格式,细节修改,所需功能基本完成完备
+### 第十次commit
+提取了读取字符串并拆分为有效单词Map的方法,提高效率
+### 第十一次commit
+改用NIO读取文件以提升性能
+### 第十二次commit
+优化了性能:采用StringTokenizer分割字符串,采用多线程
删去了之前尝试各类算法时遗留的不必要的导入
+### 第十三次commit
+合并计算字符数与单词数的类,添加注释与README文本
更改编码模式为UTF-8解决提交时的乱码问题
+
+
+
+
diff --git a/221801427/codestyle.md b/221801427/codestyle.md
new file mode 100644
index 00000000..6e7c9d1e
--- /dev/null
+++ b/221801427/codestyle.md
@@ -0,0 +1,110 @@
+## 代码规范
+
+- 缩进
+
+>1、使用的缩进的字符数为4个空格,用Tab(定义Tab键扩展成为4个空格键)来进行缩进,Tab键在不同的情况下会显示不同的长度,严重干扰阅读体验。4个空格的距离从可读性来说,正好。
+
+>2、在使用if或while等嵌套结构时,在if或while等语句下再进行Tab(扩展为4个空格)缩进,使程序看起来更清晰、美观。
+
+- 变量命名
+
+>1、局部变量使用lowerCamelCase风格,必须遵从驼峰形式。局部变量名采用英文单词的来命名,这样更能明白变量的含义。绝不使用拼音与英文混合的方式或直接使用中文的方式。
+>>正例:studentId/teacherId
+反例: DaZhePromotion [打折] / getPingfenByName() [评分] / int某变量 = 3
+
+>2、遇到含义相同的变量,会在末尾用1,2来区别。但也有例外,在使用循环变量时,因无特殊含义,仅用i,j,k来定义。
+>>正例:p1/p2/p3 和 for(i = 0;。。。)
+
+>3、在标识符命名方面,少采用缩写,若采用缩写则查询国际通用的写法,用会发音的部分来命名,并在不容易理解的缩写命名后添加注释。标识符长度跟随所要表达的含义来增减长度,以使其更容易理解。在标识符命名中,最重要的是要保持一致性——在整个程序中,对变量或是函数一类使用相同的命名规则。
+>>正例:int temp 和 int tmp
+
+- 每行最多字符数
+
+>1、每行行宽(含缩进)不允许超过100个字符。
+
+>2、当一个程序行很长时,在适当位置运用回车键将程序分行,使其不破坏语句的完整性,也使语句看起来显得不混乱、零散。
+
+- 函数最大行数
+
+>1、一个函数允许包含语句的行数我限制为80行,不包括空行和注释。
+
+>2、一个函数的最大长度和函数的复杂程度以及缩进大小成反比。
+
+- 函数、类命名
+
+>1、函数名使用lowerCamelCase风格,必须遵从驼峰形式。函数名用动词打头,以名词结尾的形式(少数会只有动词命名),使函数功能更容易理解。
+>>正例:countWord/getId
+
+>2、类名使用UpperCamelCase风格,必须遵从驼峰形式。
+>>正例:WordCount/Lib
+
+- 常量
+
+>1、常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚。
+>>正例:public static final int YEAR = 365;
+>>正例: MAX_STOCK_COUNT
+反例: MAX_COUNT
+
+>2、如果变量值仅在一个范围内变化用Enum类。如果还带有名称之外的延伸属性,必须使用Enum类,类名后缀带Enum以清晰表示,枚举成员名称需要全大写,单词间用下划线隔开。下面正例中的数字就是延伸信息,表示星期几。
+>>正例:publicEnum{MONDAY(1),TUESDAY(2),WEDNESDAY(3),
+THURSDAY(4),FRIDAY(5),SATURDAY(6),SUNDAY(7);}
+
+- 空行规则
+
+>1、变量定义和方法实现间增加空行,便于阅读。
+
+>2、在函数之间,增加空行来分割函数,这样使函数之间显得不拥挤,区分更加明显。
+
+- 注释规则
+
+>1、类、类方法的注释使用/*内容*/格式。
+
+>2、一般在变量名后,添加一行注释。
+>>正例:char option; //功能选项
+
+>3、边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。
+
+>4、注释尽量只用ASCII字符,不要用中文或其他特殊字符,利于程序的可移植性。
+
+- 操作符前后空格
+
+>1、一元运算符与变量之间没有空格
+>>正例:i++/!flag
+
+>2、二元运算符与变量之间必须有空格。
+>>正例:int i = 0;/sum = x + y;
+
+- 大括号位置
+
+>1、每个“{”和“}”都独占一行。
+>>正例:
if (condition)
+{
+ DoSomething();
+}
+else
+{
+ DoSomethingElse();
+}
+
+>2、在if等语句块较为简单时,也不省略左右大括号的使用,以使程序显得更清晰、工整。
+
+- 其他规则
+
+>1、需要使用变量时,在使用前定义,避免一些需要用到其值时,无法获得的情况。例如:
+>>正例:int i;
+ for(i = 0;。。。)
+
+>2、多个变量定义时分行写,使程序更加美观。
+>>正例:x = 1;
+ y = 2;
+ z = 3;
+
+>3、变量初始化时,提倡在尽可能小的作用域中声明变量,离第一次使用越近越好。这使得代码易于阅读,易于定位变量的声明位置、变量类型和初始值。
+>>正例:int i = 3;
+
+>4、多个不同的运算符同时存在时合理使用括号来明确优先级。
+>>例:2 << 2 + 1 * 3 - 4
+ 2 << (2 + 1 * 3 - 4)
+
+>5、避免相同的代码段在多个地方出现相同的代码,必须归纳出来并且用一个类封装起来。
+语句嵌套层次不得超过3层,超出的必须抽取出中间函数。
\ No newline at end of file
diff --git a/221801427/src/WordCount.java b/221801427/src/WordCount.java
new file mode 100644
index 00000000..5ea66f62
--- /dev/null
+++ b/221801427/src/WordCount.java
@@ -0,0 +1,95 @@
+import lib.service.*;
+import lib.tool.*;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class WordCount
+{
+ /**
+ * @param inputFileName
+ * @param outputFileName
+ */
+ private static String inputFileName;
+ private static String outputFileName;
+
+ public WordCount(String inputFileName, String outputFileName)
+ {
+ WordCount.inputFileName = inputFileName;
+ WordCount.outputFileName = outputFileName;
+ }
+
+ public void Count()
+ {
+ final String content = FileReader.readFile(inputFileName);// 从文件读取字符串
+ final HashMap words = StringAnalyser.analyseString(content);// 从字符串拆分有效单词,统计入HashMap
+
+ ExecutorService executor = Executors.newCachedThreadPool();
+
+ Future charCnt = executor.submit(new Callable()
+ {
+ // 统计字符数
+ public Integer call()
+ {
+ return CharAndWordCounter.countChar(content);
+ }
+ });
+
+ Future lineCnt = executor.submit(new Callable()
+ {
+ // 统计有效行数
+ public Integer call()
+ {
+ return LineCounter.countLine(content);
+ }
+ });
+
+ Future wordCnt = executor.submit(new Callable()
+ {
+ // 统计有效单词数
+ public Integer call()
+ {
+ return CharAndWordCounter.countWord(words);
+ }
+ });
+
+ Future>> freqList = executor
+ .submit(new Callable>>()
+ {
+ // 排序词频前10单词
+ public ArrayList> call()
+ {
+ return FrequencySorter.sortFrequency(words);
+ }
+ });
+
+ try
+ {
+ FilePrinter.writeFile(charCnt.get(), wordCnt.get(), lineCnt.get(), freqList.get(), outputFileName);// 打印结果
+ executor.shutdown();
+ }
+ catch (InterruptedException | ExecutionException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ WordCount cmd;
+
+ if (args.length != 2)
+ {
+ System.out.println("Invalid input");
+ return;
+ }
+ cmd = new WordCount(args[0], args[1]);// 传入参数(输入输出文件名)
+ // cmd = new WordCount("src/input.txt", "src/output.txt");
+ cmd.Count();// 统计
+ }
+}
diff --git a/221801427/src/lib/service/CharAndWordCounter.java b/221801427/src/lib/service/CharAndWordCounter.java
new file mode 100644
index 00000000..f45229ca
--- /dev/null
+++ b/221801427/src/lib/service/CharAndWordCounter.java
@@ -0,0 +1,24 @@
+package lib.service;
+
+import java.util.HashMap;
+
+public class CharAndWordCounter
+{
+ /**
+ * @param content
+ * @return 字符总数
+ */
+ public static int countChar(String content)
+ {
+ return content.length();// 直接返回字符串长度
+ }
+
+ /**
+ * @param words
+ * @return 单词总数
+ */
+ public static int countWord(HashMap words)
+ {
+ return words.size();// 返回HashMap规模表示有效单词总数
+ }
+}
diff --git a/221801427/src/lib/service/FrequencySorter.java b/221801427/src/lib/service/FrequencySorter.java
new file mode 100644
index 00000000..139ec497
--- /dev/null
+++ b/221801427/src/lib/service/FrequencySorter.java
@@ -0,0 +1,29 @@
+package lib.service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class FrequencySorter
+{
+ public static final int MAX_SIZE = 10;
+
+ /**
+ * @param words
+ * @return freqList
+ */
+ public static ArrayList> sortFrequency(HashMap words)
+ {
+ words = words.entrySet().stream()
+ .sorted(Map.Entry.comparingByValue()// 值排序
+ .reversed()// 倒序为降序
+ .thenComparing(Map.Entry.comparingByKey()))// 键排序(字典序)
+ .limit(MAX_SIZE)// 前MAX_SIZE个
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
+ (oldVal, newVal) -> oldVal, LinkedHashMap::new));// 返回map
+
+ ArrayList> freqList = new ArrayList>(
+ words.entrySet());
+
+ return freqList;
+ }
+}
diff --git a/221801427/src/lib/service/LineCounter.java b/221801427/src/lib/service/LineCounter.java
new file mode 100644
index 00000000..9043817d
--- /dev/null
+++ b/221801427/src/lib/service/LineCounter.java
@@ -0,0 +1,28 @@
+package lib.service;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class LineCounter
+{
+ private static final String VALID_LINE_REGEX = "(^|\n)\\s*\\S+";
+ private static final Pattern VALID_LINE_PATTERN = Pattern.compile(VALID_LINE_REGEX);
+
+ /**
+ * @param content
+ * @return 有效行数
+ */
+ public static int countLine(String content)
+ {
+ int cnt = 0;
+ Matcher matcher;
+
+ matcher = VALID_LINE_PATTERN.matcher(content);
+ while (matcher.find())
+ {
+ //利用正则表达式匹配有效行,空行不统计,包括\t \r \n 空格以及由他们组成的情况
+ cnt++;
+ }
+ return cnt;
+ }
+}
diff --git a/221801427/src/lib/service/StringAnalyser.java b/221801427/src/lib/service/StringAnalyser.java
new file mode 100644
index 00000000..c6b687dd
--- /dev/null
+++ b/221801427/src/lib/service/StringAnalyser.java
@@ -0,0 +1,45 @@
+package lib.service;
+
+import java.util.*;
+import java.util.regex.Pattern;
+
+public class StringAnalyser
+{
+ private static final String FILTER_REGEX = "[^0-9A-Za-z]";
+ private static final String VALID_WORD_REGEX = "[a-z]{4}[0-9a-z]*";
+
+ /**
+ * @param content
+ * @return words
+ */
+ public static HashMap analyseString(String content)
+ {
+ int cnt; // 临时变量用于计入键值(单词出现次数)
+ String word;
+
+ HashMap words = new HashMap();
+ StringTokenizer tokenizer = new StringTokenizer(content.replaceAll(FILTER_REGEX, " "));
+ // 先将分隔符全替换为空格,再利用 StringTokenizer 切分单词
+
+ while (tokenizer.hasMoreTokens())
+ {
+ word = tokenizer.nextToken(" ");
+ if (Pattern.matches(VALID_WORD_REGEX, word))
+ {
+ // 利用正则表达式统计有效字符:至少有四位且都为字母,后跟若干字母或数字,不区分大小写,计入HashMap。
+ if (words.containsKey(word))
+ {
+ // 单词已统计到过
+ cnt = words.get(word);
+ words.put(word, cnt + 1);
+ }
+ else
+ {
+ // 单词初次统计到
+ words.put(word, 1);
+ }
+ }
+ }
+ return words;
+ }
+}
diff --git a/221801427/src/lib/tool/FilePrinter.java b/221801427/src/lib/tool/FilePrinter.java
new file mode 100644
index 00000000..1fc79793
--- /dev/null
+++ b/221801427/src/lib/tool/FilePrinter.java
@@ -0,0 +1,50 @@
+package lib.tool;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class FilePrinter
+{
+ /**
+ * @param charCnt
+ * @param wordCnt
+ * @param lineCnt
+ * @param freqList
+ * @param outputFileName
+ */
+ public static void writeFile(int charCnt, int wordCnt, int lineCnt,
+ ArrayList> freqList, String outputFileName)
+ {
+ // 接收需要统计的数值并打印
+ File file = new File(outputFileName);
+ FileWriter fileWriter = null;
+ BufferedWriter bufferedWriter = null;
+ try
+ {
+ fileWriter = new FileWriter(file);
+ bufferedWriter = new BufferedWriter(fileWriter);// 使用BufferedWriter提高性能
+
+ bufferedWriter.write("characters: " + charCnt + "\n");
+ bufferedWriter.write("words: " + wordCnt + "\n");
+ bufferedWriter.write("lines: " + lineCnt + "\n");
+ for (HashMap.Entry map : freqList)
+ {
+ bufferedWriter.write(map.getKey() + ": " + map.getValue() + "\n");// 打印HashMap的键与对应值
+ }
+ bufferedWriter.flush();
+ bufferedWriter.close();
+ }
+ catch (FileNotFoundException e)
+ {
+ System.out.println("File Not Found");
+ e.printStackTrace();
+ }
+ catch (IOException e)
+ {
+ System.out.println("Error Writing File");
+ e.printStackTrace();
+ }
+ }
+}
+
diff --git a/221801427/src/lib/tool/FileReader.java b/221801427/src/lib/tool/FileReader.java
new file mode 100644
index 00000000..08f8e069
--- /dev/null
+++ b/221801427/src/lib/tool/FileReader.java
@@ -0,0 +1,62 @@
+package lib.tool;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.FileChannel;
+import java.nio.charset.StandardCharsets;
+
+import sun.misc.Cleaner;
+import sun.nio.ch.DirectBuffer;
+
+public class FileReader
+{
+ /**
+ * @param inputFileName
+ * @return 读取出的字符串
+ */
+ @SuppressWarnings("resource")
+ public static String readFile(String inputFileName)
+ {
+ //通过MappedByteBuffer读取文件
+ File file = new File(inputFileName);
+ long len = file.length();
+ MappedByteBuffer mappedByteBuffer = null;
+
+ try
+ {
+ mappedByteBuffer = new RandomAccessFile(file, "r").getChannel().map(FileChannel.MapMode.READ_ONLY, 0, len);
+ // 通过RandomAccessFile获取FileChannel,并通过FileChannel.map方法,把文件映射到虚拟内存,返回逻辑地址。
+ if (mappedByteBuffer != null)
+ {
+ return StandardCharsets.UTF_8.decode(mappedByteBuffer).toString().toLowerCase();
+ }
+ else
+ {
+ return "";// 空白文件则返回空
+ }
+ }
+ catch (FileNotFoundException e)
+ {
+ System.out.println("File Not Found");
+ e.printStackTrace();
+ }
+ catch (IOException e)
+ {
+ System.out.println("Error Reading File");
+ e.printStackTrace();
+ }
+ finally
+ {
+ if (mappedByteBuffer != null)
+ {
+ // 垃圾回收
+ Cleaner cleaner = ((DirectBuffer) mappedByteBuffer).cleaner();
+ if (cleaner != null)
+ {
+ cleaner.clean();
+ }
+ }
+ }
+ return "";// 未读取到则返回空
+ }
+}