词频统计的java实现方法——第一次改进

需求概要

原需求

1.读取文件,文件内包可含英文字符,及常见标点,空格级换行符。

2.统计英文单词在本文件的出现次数

3.将统计结果排序

4.显示排序结果

新需求:

1.小文件输入. 为表明程序能跑

2.支持命令行输入英文作品的文件名

3. 支持命令行输入存储有英文作品文件的目录名,批量统计。

4. 从控制台读入英文单篇作品

程序输入:

1.控制台输入文本

2.英文文本文件

3.英文目录,目录下包含单个或多个英文文本文件

程序输出:

1.英文单词在本文件或控制台输入中的出现的次数,按出现次数排序

2.文本或控制台输入文字的单词数和不重复单词数

输出位置:

控制台或指定文件

支持统计的词包括:纯英文单词和字母开头夹杂数字的单词,例如:happy,x11,i386,w3c等

程序的使用设计

1.程序直接运行,不加参数,则进入标准输入状态,所有标准输入作为词频统计分析的内容,windows下回车加ctrl+Z作为退出输入模式的出口,然后对输入缓存进行统计

2.使用选项参数

默认情况下接受文件名或文件夹作为输入,读入单独或文件夹下的多个文件

-o可以指定输出文件名,即保存统计结果到文件中,不适用-o选项则将输出结果打印到终端或控制台,

-s出现此选项则将之后的输入视为标准输入,作为分析的内容。

主要功能设计和分析

前一部分的内容不在此赘述,以下为更新后的功能设计

不同输入逻辑处理

根据最新需求,本程序需要处理来自文本和控制台的输入,需要对程序运行的参数和方法进行控制,java程序的main函数String[] args可以接收程序运行的参数,当参数为空时则进入标准输入读入的模式,有参数的时候根据不同参数进行操作。

为简化逻辑,参数的读入和初始化可以建立初始化器来使用的参数进行环境设置或者初始化,main函数中则根据已经设定好的环境来决定从哪读入向哪输出。

缓冲读取文件或标准输入流,统计词频

当程序参数为空时需要将标准输入作为待分析内容,java提供的System.in可作为标准输入,读入时可以使用byte[]来接收。

当程序读入文件时可以使用char[]来接收读取到的内容,这与标准输入流略有不同,不过可以通过类型转换的方式来实现重用。

词频统计与之前的区别在于,使用BufferedReader提供的read来替换readline来解决当行过长导致的字符串溢出问题,这样就需要定义一个缓冲区来分块读取文件或输入流的内容,读取到的缓冲区再模仿状态机的处理方式来确定读入的字符该如何处理

多文件的读取

java文件类提供判断一个路径是不是文件夹的函数即File.isDirectory(),如果确定输入为文件夹则使用File.listFiles()获取到文件夹下的所有文件,然后循环分析词频,根据已经创建好的环境决定输出到文件还是控制台。

部分代码实现

初始化器实现

     String inputFile = null;
String outputFile = null;
boolean stdin = false; private boolean setArgs(String[] args) {
try {
if (args.length == 0) {
stdin=true;
return true;
}
boolean isOption = false;
for (int i = 0; i < args.length; i++) {
if (args[i].startsWith("-")) {
isOption = true;
if (args[i].equals("-o")) {
outputFile = args[i + 1];
} else if(args.equals("-s")){
stdin = true;
return true;
}else{
System.out.println("unknow commend:" + args[i]);
return false;
}
} else if (isOption) {
// setOpetion
isOption = false;
} else if (inputFile == null) {
inputFile = args[i];
} else {
System.out.println("wrong command");
return false;
}
}
System.out.println("inputfile = " + inputFile);
System.out.println("onputfile = " + outputFile);
if (inputFile == null) {
System.out.println("no input file name");
return false;
}
} catch (Exception e) {
System.out.println("wrong command");
return false;
}
return true;
}

更新后的词频统计

文件方式作为输入

     public Map<String, Integer> getWordGroupCountBuffered(String filename) {
try {
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);
StringBuffer content = new StringBuffer("");
Map<String, Integer> result = new HashMap<String, Integer>();
char[] ch = new char[64];
int bs = 0;
int idx;
boolean added = false;
total = 0;
while ((bs = br.read(ch)) > 0) {
for (idx = 0; idx < bs; idx++) {
if (isCharacter(ch[idx])==1) {
content.append(ch[idx]);
added = false;
} else if(isCharacter(ch[idx])==2){
if(added == true){
continue;
}else{
content.append(ch[idx]);
}
} else {
if(added==true){
continue;
}
added = true;
if(content.equals(""))
continue;
String key = content.toString();
if (result.containsKey(key))
result.put(key, result.get(key) + 1);
else
result.put(key, 1);
total++;
content = new StringBuffer("");
continue;
} }
}
br.close();
fr.close();
return result;
} catch (FileNotFoundException e) {
System.out.println("failed to open file:" + filename);
e.printStackTrace();
} catch (Exception e) {
System.out.println("some expection occured");
e.printStackTrace();
}
return null;
}

标准输入作为输入

     public Map<String, Integer> getWordGroupCountBuffered(InputStream in) {
try {
StringBuffer content = new StringBuffer("");
Map<String, Integer> result = new HashMap<String, Integer>();
byte[] bt = new byte[128];
char [] ch = new char[64];
int bs = 0;
int idx;
int cs = 0;
boolean added = false;
total = 0;
while ((bs = in.read(bt)) > 0) {
ch = byteToChar(bt,bs);
cs = bs;
for (idx = 0; idx < cs; idx++) {
if (isCharacter(ch[idx])==1) {
content.append(ch[idx]);
added = false;
} else if(isCharacter(ch[idx])==2){
if(added == true){
continue;
}else{
content.append(ch[idx]);
}
} else {
if(added==true){
continue;
}
added = true;
if(content.equals(""))
continue;
String key = content.toString();
if (result.containsKey(key))
result.put(key, result.get(key) + 1);
else
result.put(key, 1);
total++;
content = new StringBuffer("");
continue;
} }
}
return result;
} catch (Exception e) {
System.out.println("some expection occured");
e.printStackTrace();
}
return null;
}

文件夹下多文件统计函数头为

public void printSortedWordGroupCountToFileBuffered(File[] files, String destinationFilename)

实现方式基本同单个文件统计方式,仅需添加循环遍历和输出文件方式设定为追加,并且第一次打开时清空文件内容,防止多次执行同一命令时重复追加到一个文件。

主函数逻辑控制

     public static void main(String[] args) {
RunFileWordUtil rfu = new RunFileWordUtil();
if (!rfu.setArgs(args)) {
return;
}
File f = null;
FileWordUtil fu = new FileWordUtil();
if(rfu.stdin){
fu.printSortedWordGroupCountBuffered(System.in);
}else{
f = new File(rfu.inputFile);
if(f.isDirectory()){
File[] files = f.listFiles();
if(rfu.outputFile == null){
System.err.println("warning : print to console is not recomanded");
fu.printSortedWordGroupCountBuffered(files);
}else{
fu.printSortedWordGroupCountToFileBuffered(files,rfu.outputFile);
}
}else if (rfu.outputFile == null) {
System.err.println("warning : print to console is not recomand");
fu.printSortedWordGroupCountBuffered(rfu.inputFile);
}else {
fu.printSortedWordGroupCountToFileBuffered(rfu.inputFile, rfu.outputFile);
}
}
}

其他被调用的部分自定义函数

     private int isCharacter(char ch) {
if ((ch >= 'a' && ch <= 'z'))
return 1;
if((ch >= 'A' && ch <= 'Z'))
return 1;
if(ch>='0'&&ch<='9')
return 2;
return 0;
} private char[] byteToChar(byte[] bt,int length){
char[] ch = new char[length];
for(int i=0;i<length;i++){
ch[i]=(char)bt[i];
}
return ch;
}

实际运行结果

1.标准输入作为输入内容

词频统计的java实现方法——第一次改进

2.单个文件作为输入内容

文件内容:

hello world
this is a
what a sunny day!
do
you
have a cup? read the book My English is very very pool.  

warning为标准错误输出,在支持彩色显示的终端中可以显示为红色(或其他自定义颜色)

词频统计的java实现方法——第一次改进

3.文件夹下的所有文件作为输入内容

词频统计的java实现方法——第一次改进

4.结果输出到文件

词频统计的java实现方法——第一次改进

5.大文件统计:

文件内容:英文版WarandPeace.txt

词频统计的java实现方法——第一次改进

工程地址https://coding.net/u/jx8zjs/p/wordCount/git

ssh://git@git.coding.net:jx8zjs/wordCount.git

上一篇:效能分析——词频统计的java实现方法的第一次改进


下一篇:Java实现的词频统计——功能改进