Java文本处理7-查询文本中单个汉字的信息熵

1、任务简介

本节任务的目的是编写一个Java程序,用于求取文本文档中某一指定汉字的信息熵,该任务可以基于《Java文本处理3-统计文本行数、字符数、汉字、数字、字母数等》《Java文本处理4-查询文本中汉字或字符串的出现次数》来进行。


2、信息熵的计算

关于信息熵的概念我们在《信息论》这门课程中已经学习过了,信息熵具有确定性、非负性、对称性、连续性等许多重要性质,所以我们在计算它的时候要保证计算结果为正值,其基本公式为:
H(X)=i=1npiIi=i=1npilog2(pi) H(X)=\sum_{i=1}^{n}{p_i*I_i}=-\sum_{i=1}^{n}{p_i*\log_2(p_i)}
其中p为概率,I为信息量。
由于信息熵的计算中对数是以2为底的,所以需要使用到换底公式,求该汉字信息熵的实现代码如下:

double H = -p * (Math.log(p) / Math.log(2));//计算该汉字的信息熵

3、具体任务和代码

1)具体思路

(1)首先需要使用InputStreamReader类和BufferedReader类实现文本的读取,由于我使用的《西游记》为utf-8编码,所以还需要指定编码格式为utf-8;

(2)然后需要定义一个空的字符串变量,在对文本逐行读出后将读出的内容追加到该空字符串后;

(3)逐行读出文档,然后使用for循环对该行中的每一个字符进行遍历,使用toString()方法得到每一个字符,再使用if语句和matches()方法检测字符是否为汉字,若匹配则对指定的变量进行自增,求出总的汉字数;

(4)使用Pattern类和Matcher类进行字符的匹配操作,其中Pattern类可以创建一个正则表达式,该正则表达式的内容为用户想要查询的字符串;Matcher类可以创建一个匹配器,需要匹配的内容为整个文本,然后需要对匹配次数进行统计,这就需要使用matcher.find()方法,每次匹配均会返回一个布尔变量,若成功则为true,配合while循环就可以对出现次数进行统计,并求出该汉字在该文本文档的出现次数;

(5)最后计算该汉字在文本文档中的出现次数、概率及信息熵,并将结果打印出来;

2)任务代码

程序保存为shang1.java,代码如下:

import java.io.*;//导入java.io包中的所有类
import java.util.Scanner;//导入java.util包中的Scanner类
import java.util.regex.Matcher;//导入java.util.regex包中的Matcher类
import java.util.regex.Pattern;//导入java.util.regex包中的Pattern类
public class shang1 {//类名
    public static void main(String[] args)  {//程序主函数
        try {//try代码块,当发生异常时会转到catch代码块中
        	//读取指定的文件
        	Scanner s = new Scanner(System.in);//创建scanner,控制台会一直等待输入,直到敲回车结束
        	Scanner x = new Scanner(System.in);//创建scanner,控制台会一直等待输入,直到敲回车结束
        	System.out.println("请输入想要打开的文本文档:");//输入提示信息
        	String a = s.nextLine();//定义字符串变量,并赋值为用户输入的信息
        	System.out.println("请输入想要查询的汉字:");//输入提示信息
	    	String b = x.nextLine();//定义字符串变量,并赋值为用户输入的信息
        	//创建类进行文件的读取,并指定编码格式为utf-8
        	InputStreamReader read = new InputStreamReader(new FileInputStream(a),"utf-8"); 
            BufferedReader in = new BufferedReader(read);//可用于读取指定文件     
			StringBuffer c = new StringBuffer();//定义一个字符串变量c,便于后续进行内容追加的操作
			String str = null;//定义一个字符串类型变量str
			String d=null;//定义一个字符串类型变量d
			double i = 0;//定义一个double型变量,用于统计指定汉字的出现次数
			double e = 0;//定义一个double型变量,用于统计总汉字数
			while((str = in.readLine()) != null) {//readLine()方法, 用于读取一行,只要读取内容不为空就一直执行
				c.append(str);//将该行内容追加到字符串c的后面
				for (int j = 0; j < str.length(); j++) {//for循环的条件,当j小于该行长度时就一直循环并自增
            		d = Character.toString(str.charAt(j));//返回一个字符串对象
            		if (d.matches("[\\u4e00-\\u9fa5]")) {//if语句的条件,判断是否为汉字
                        e++;//若为汉字则e自增
                    }
			    }
			}
			Pattern pattern = Pattern.compile(b);//创建一个正则表达式
			Matcher matcher = pattern.matcher(c);//创建一个匹配器
			while(matcher.find()) {//while循环的条件,若matcher.find()为true即匹配成功
				i++;//若匹配成功则i自增
			}
            in.close();//关闭流
            double p = i/e;//定义一个double型变量,计算该汉字在文章中的出现概率
            double H = -p * (Math.log(p) / Math.log(2));//定义一个double型变量,计算该汉字的信息熵
            System.out.println("该文本共有"+e+"个汉字");//输出总的字符数
            System.out.println(b+"在文本中出现了"+i+"次");//输出总的出现次数
            System.out.println(b+"的出现概率为"+p);//输出该汉字在文本中的出现概率
            System.out.println(b+"的信息熵为"+H+"比特/符号");//输出该汉字的信息熵
        } catch (IOException e) {//当try代码块有异常时转到catch代码块
        	e.printStackTrace();//printStackTrace()方法是打印异常信息在程序中出错的位置及原因
        }
    }
}

3)运行结果

(1)所有文件均保存在路径D:\demo5下,在命令行中对程序进行编译,然后运行程序读取路径下的txt文档,查询汉字“道”,结果如下:
Java文本处理7-查询文本中单个汉字的信息熵
从图中可以看出我使用的原版《西游记》txt文档包含字符“道”7223次,并且“道”字的出现概率和信息熵也求出来了。
(2)查询汉字“一”,结果如下:
Java文本处理7-查询文本中单个汉字的信息熵
从上面两幅图中可以看出该程序可以计算出该文本的总汉字数以及指定汉字的在文本中的出现次数、概率和信息熵,并且对总汉字数与指定汉字出现次数的计算结果与之前任务的计算结果一致。


4、总结

信息熵的计算对于文本的处理具有重要的意义,本次任务的完成使得我们可以查询任一文本文档中某一指定汉字的信息熵,这也是是后续学习的基础。