假设我们有一段英文文本,我们希望统计出其中每个单词数量,并且以此画出一幅统计图(使用JAVAFX实现),我们该怎么做?
注:只支持统计英文文本
最后的效果如下(表格太长,只展示部分):
完整的代码放在文章结尾,大家可以先复制看下效果。
对于这道题目,我将它分为三个部分:
1. 实现对文本的读写
java中对文本的阅读方式有很多,也有很多讲解,所以这里我不做过多的例举,这里直接展示我的读文本代码:
值得注意的是,这里我们将读下来的文本存放在字符串中,并且返回,等待处理。
// 读文件
private static String readString3(String fileName)
{
String str = "";
File file = new File(fileName);
try {
FileInputStream in = new FileInputStream(file);
// size 为字串的长度 ,这里一次性读完
int size = in.available();
byte[] buffer = new byte[size];
in.read(buffer);
in.close();
str = new String(buffer, "GB2312");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
return str;
}
2. 在对文本进行处理后,实现单词个数的统计
对于这个部分,我们也应该有清晰的思路:
1. 我们的图表需要什么数据?又要这怎样存储这些数据?
很显然,对于一个柱状图,无非是一 一对应的横纵坐标,因此我们需要两个List,一个存储单词(不重复),一个存储单词的数量,两者的数据一一对应。
比如,对于上图,我们可以读出:我们又7个secure,20个a…
由此我们解决了第一个大问题,那么我们如何得到这两个List呢?
2. 我们如何得到这些数据?
要想统计每个单词的个数,首先要让字符串中只剩下英文字符,然后筛选掉重复的单词,因此,我总结了一下筛选步骤:
- 替换所有的符号到空格
- 将所有字符转化位小写
- 分割字符串
- 单词去重
- 去除数字的字符串
- 统计对应单词数量
这里还要插入小段内容,为了实现键盘输入文件名,我又加了一下段代码:
Scanner input = new Scanner(System.in);
System.out.println("请输入Java源文件名");
String fileName = input.next();
File file = new File(fileName);
if (!file.exists()) {
System.out.println("节点基本信息文件未找到");
System.exit(0);
}
//str1接受含文本内容的字符串
String str1 = readString3(fileName);
接下来我们来详细说一下如何筛选:
1. 替换所有的符号到空格
这里的实现很简单,我们将所有待替换字符放在一个字符数组Eroor_str中,如果str1中的字符在Eroor_str中,我们就替换该字符位空格。
String Eroor_str = "~@#$%^&*()_-+=<>?/,.:;{}[]|\\'\"\"\"\r\n";
for (int i = 0; i < Eroor_str.length(); i++) {
str1 = str1.replace(Eroor_str.charAt(i), ' ');
}
2. 将所有字符转化位小写
对于大小写的字母,计算机会识别为两个单词,所以我们得吧大小写统一,这里我们直接调用库函数 toLowerCase()
str1 = str1.toLowerCase();
3. 分割字符串
经过上面两步,我们要进行关键的一步。
我们认定一个单词的依据是 该字符串前后均为空格(暂不考虑该字符串的语义是否存在),所以我们对每个空格处进行切割,这样我就的到了所有的单个单词。
String[] arr = str1.split(" ");
split()函数可以将字符串分割为子字符串,我们只需要用一个字符串数组去接受即可。
4. 去重
接着我们将这个 字符串数组 强制转化 为一个List,并且新建一个List,命名为result,这个List我们用来存储不重复的所有单词。
我们将list中的数据一个一个添加到result中,但是,在加入元素之前时,我们要判断该元素是否已经在result中存在,如果不存在,就加入,反之不加入。
这样,我们就实现了“去重”
List<String> list = Arrays.asList(arr);
List<String> result = new ArrayList<String>(list.size());
for (String str : list) {
if (!result.contains(str)) {
result.add(str);
}
}
5. 去除数字的字符串
我们先不管list这个List,我们对result进行处理:
我们还需要将数字字符串和含字符的字符串筛选掉,下面的三句代码很简洁的达到效果,这里限于篇幅,不过多讲解。
Pattern pattern = Pattern.compile("\\d");// 匹配数字
Predicate<String> filter = s -> pattern.matcher(s).find();
result.removeIf(filter);
6. 统计对应单词的数量
这一步也是重要的一步,思考了很久,如何在不用哈希和正则表达式的情况下进行筛选。
我们现在有两个LIst:
- list,存储了所有的单词
- result,存储了所有去重后的单词
这样问题逻辑变得简单粗暴:我们将result的单词以此拿出来 与list中的单词逐个比较,相同就加一即可,最终将所有单词数目存在一个数组中。
int wordCounts[] = new int[result.size()];
for (int i = 0; i < arr.length; i++) {
System.out.print("/" + arr[i] + "/");
}
System.out.print("\n");
System.out.print(list);
System.out.print("\n");
System.out.print(result);
System.out.print("\n");
for (int i = 0; i < result.size(); i++) {
wordCounts[i] = 0;
for (int j = 0; j < list.size(); j++) {
if (list.get(j).equals(result.get(i))) {
wordCounts[i] += 1;
}
}
}
值得注意的是,我们在比较元素是否相等的时候,不能使用==,而是要使用
equal()方法去比较。否则我们会得到所有的计数都为0.
原因在于,在JAVA中,String 不是一个基本的数据类型,而是一个引用数据类型,对于运算符==来说,它的运算规则如下:
== :运算符
1.可以使用基本运算类型和引用数据类型变量中
2.如果比较的是基本数据类型,比较两个变量保存的数据是否相等
如果比较的是引用数据类型:比较的是两个数据得到地址值是否相同
所以我们对两个String类型使用== ,比较的是地址,而不是数值。
所以我们要是用equal()函数进行内容比较,虽然equal的本质也是==。但系统对于 像String,Date,File,包装类等都重写了Object类中的equals()方法 ,重写之后,比较的不是两个引用的地址是否相同,而是比较两个对象的“实体内容”是否相同
最后我们想要同时返回result和wordCount.
显然不行,所以我稍作处理,将两者合并:
效果如下:
List list2 = new ArrayList<>();
System.out.println("\n");
for (int i = 0; i < result.size(); i++) {
list2.add(result.get(i));
list2.add((wordCounts[i]));
}
System.out.println(list2);
return list2;
3. 使用JAVAFX进行绘图
对于Javafx,我不做过多的讲解,有很多教程。
public void start(Stage primaryStage) throws Exception {
List list2 = new ArrayList<>();// 存數字
List<String> list3 = new ArrayList<String>();// 存單詞
List<String> list = TurnInto();// 存儲了所有單詞的list(不重複)
for (int i = 3; i < list.size(); i = i + 2) {
list2.add(list.get(i));
}
for (int i = 2; i < list.size(); i = i + 2) {
list3.add(list.get(i));
}
// 定义坐标轴,X轴表示比较的类别,y轴表示出现次数
// Defining the x axis
CategoryAxis xAxis = new CategoryAxis();
// xAxis.setCategories(FXCollections.<String>observableArrayList(list3));
xAxis.setLabel("词");
// Defining the y axis
NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("出现次数");
// 创建条形图
// Creating the Bar chart
BarChart<String, Number> barChart = new BarChart<>(xAxis, yAxis);
barChart.setTitle("词频统计图");
//barChart.setMaxWidth(5000);
barChart.setMinSize(10000, 50);
for (int i = 0; i < list3.size(); i++) {
XYChart.Series<String, Number> series1 = new XYChart.Series<>();
series1.setName(list3.get(i));
series1.getData().add(new XYChart.Data<>(list3.get(i), (int) list2.get(i)));
barChart.getData().addAll(series1);
}
// 创建组对象
Group root = new Group(barChart);
// 创建场景对象
Scene scene1 = new Scene(root, 10000, 300);
// 设置舞台的标题
primaryStage.setTitle("詞頻統計表");
// 将场景添加到舞台
primaryStage.setScene(scene1);
// 显示舞台的内容
primaryStage.show();
// 启动应用程序
}
完整代码
package WordCounts;
import java.util.Scanner;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.stage.Stage;
import java.io.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javafx.scene.Group;
//javaFX图形化包
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
//abcd.txt
public class test2 extends Application {
class CustomPane extends StackPane{//定义了StackPane的CustomPane类,
public CustomPane(String title) {
getChildren().add(new Label(title));//加入一个标签(标题)
setStyle("-fx-border-color:red");//为边框颜色设置样式
setPadding(new Insets(11.5,12.5,13.5,14.5));//设置内边距
}
}
public void start(Stage primaryStage) throws Exception {
List list2 = new ArrayList<>();// 存數字
List<String> list3 = new ArrayList<String>();// 存單詞
List<String> list = TurnInto();// 存儲了所有單詞的list(不重複)
for (int i = 3; i < list.size(); i = i + 2) {
list2.add(list.get(i));
}
for (int i = 2; i < list.size(); i = i + 2) {
list3.add(list.get(i));
}
// 定义坐标轴,X轴表示比较的类别,y轴表示出现次数
// Defining the x axis
CategoryAxis xAxis = new CategoryAxis();
// xAxis.setCategories(FXCollections.<String>observableArrayList(list3));
xAxis.setLabel("词");
// Defining the y axis
NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("出现次数");
// 创建条形图
// Creating the Bar chart
BarChart<String, Number> barChart = new BarChart<>(xAxis, yAxis);
barChart.setTitle("词频统计图");
//barChart.setMaxWidth(5000);
barChart.setMinSize(5000, 50);
for (int i = 0; i < list3.size(); i++) {
XYChart.Series<String, Number> series1 = new XYChart.Series<>();
series1.setName(list3.get(i));
series1.getData().add(new XYChart.Data<>(list3.get(i), (int) list2.get(i)));
barChart.getData().addAll(series1);
}
// 创建组对象
Group root = new Group(barChart);
// 创建场景对象
Scene scene1 = new Scene(root, 5000, 300);
// 设置舞台的标题
primaryStage.setTitle("詞頻統計表");
// 将场景添加到舞台
primaryStage.setScene(scene1);
// 显示舞台的内容
primaryStage.show();
// 启动应用程序
}
// 读文件
private static String readString3(String fileName)
{
String str = "";
File file = new File(fileName);
try {
FileInputStream in = new FileInputStream(file);
// size 为字串的长度 ,这里一次性读完
int size = in.available();
byte[] buffer = new byte[size];
in.read(buffer);
in.close();
str = new String(buffer, "GB2312");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
return str;
}
static List<String> TurnInto() {
Scanner input = new Scanner(System.in);
System.out.println("请输入Java源文件名");
String fileName = input.next();
File file = new File(fileName);
if (!file.exists()) {
System.out.println("节点基本信息文件未找到");
System.exit(0);
}
String str1 = readString3(fileName);
// 替换所有符号到空格
String Eroor_str = "~@#$%^&*()_-+=<>?/,.:;{}[]|\\'\"\"\"\r\n";
for (int i = 0; i < Eroor_str.length(); i++) {
str1 = str1.replace(Eroor_str.charAt(i), ' ');
}
// 将所有字符转化为小写
str1 = str1.toLowerCase();
// 分割字符串
String[] arr = str1.split(" ");
List<String> list = Arrays.asList(arr);
List<String> result = new ArrayList<String>(list.size());
for (String str : list) {
if (!result.contains(str)) {
result.add(str);
}
}
// 去除帶數字的字符串
Pattern pattern = Pattern.compile("\\d");// 匹配数字
Predicate<String> filter = s -> pattern.matcher(s).find();
result.removeIf(filter);
// 存儲每個字符串的個數的數組
int wordCounts[] = new int[result.size()];
for (int i = 0; i < arr.length; i++) {
System.out.print("/" + arr[i] + "/");
}
System.out.print("\n");
System.out.print(list);
System.out.print("\n");
System.out.print(result);
System.out.print("\n");
for (int i = 0; i < result.size(); i++) {
wordCounts[i] = 0;
for (int j = 0; j < list.size(); j++) {
if (list.get(j).equals(result.get(i))) {
wordCounts[i] += 1;
}
}
}
for (int i = 0; i < result.size(); i++) {
System.out.print(wordCounts[i] + " ");
}
List list2 = new ArrayList<>();
System.out.println("\n");
for (int i = 0; i < result.size(); i++) {
list2.add(result.get(i));
list2.add((wordCounts[i]));
}
System.out.println(list2);
return list2;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
// List<String> list = TurnInto();
Application.launch(args); // 启动这个程序 APPlication的方法
//Application.launch(ScannerInterface.class);
}
}