本内容基于txt文本进行统计,对于单词的时态,人称和拼写变种等直接使用2+2+3lem 替换,舍去撇号缩写单词后半部分(形如we’re只保留we),保留hyphen(-)连接的单词,并对结果进行翻译。
文本解析时进行正则表达式替换,然后将所得结果按空格扫描输入,按<单词, 词频>输入Map中,然后对Map进行按词频排序,再格式化输出到文本中。
相关资源下载:
- 时态,人称和拼写变种匹配词典:2+2+3lem.txt
- txt英汉词典:stardict1.3.txt
- 源码下载:
结果示例:
统计于The Old Man and The Sea,与网上的一些版本有较大不同,主要来源于时态,单复数和大小写等的替换
总字数:26590 单词数:1790 @xinyo.org
序号 单词 次数 万分比 翻译
---- ---- ---- ---- ----
1 the 2316 871 art. 那;
2 he 1396 525 int. 他;
3 and 1259 473 conj. 和,与;
4 be 1001 376 prep. 是,有,在;
5 I 567 213 int. 我;
6 of 540 203 prep. 属于;
7 it 494 185 int. 它;
8 to 454 170 prep. 到,向;
9 his 446 167 pron. 他的;
10 a 426 160 art. 一;
1. 读取txt文件
目标txt文件直接放在了Project的跟文件夹下,默认读取中文乱码,所以添加了codeType
形参的read()方法
package org.xinyo.GetFreq;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class ReadFile {
public static String read(String file){
StringBuilder sb = new StringBuilder();
try {
InputStreamReader isr =
new InputStreamReader(new FileInputStream(file));
BufferedReader in = new BufferedReader(isr);
try {
String s;
while((s = in.readLine()) != null) {
sb.append(s);
sb.append("\n");
}
} finally {
in.close();
}
} catch(IOException e) {
throw new RuntimeException(e);
}
return sb.toString();
}
//codeType是为了考虑中文,单词翻译过程中使用
public static String read(String file, String codeType){
StringBuilder sb = new StringBuilder();
try {
InputStreamReader isr =
new InputStreamReader(new FileInputStream(file),codeType);
BufferedReader in = new BufferedReader(isr);
try {
String s;
while((s = in.readLine()) != null) {
sb.append(s);
sb.append("\n");
}
} finally {
in.close();
}
} catch(IOException e) {
throw new RuntimeException(e);
}
return sb.toString();
}
}
2. 词典初始化(2+2+3lem.txt)
词典原始地址:2+2+3lem ,我选择了用这个穷举替换的方法。实际上 英文分词算法(Porter stemmer)有很多其他成熟的方案可以直接使用,如文章http://lutaf.com/212.htm 中提到的:
以上这些方法涉及语言学知识,我也没有深究(也看不懂),这些算法可以直接使用。
本方案中将变种写入Key,对应的原型写入Value,可能会有不同的Key对应相同的Value值
package org.xinyo.GetFreq;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;
import java.util.regex.Pattern;
public class Dict {
public static HashMap<String, String> readDict(String pathdict){
String dict = ReadFile.read(pathdict);//读取词典文件
Pattern pt = Pattern.compile("\\s{4}.+");//词典变种单词行匹配
ArrayList<String> arrL = new ArrayList<>();
HashMap<String, String> dictMap = new HashMap<>();
Scanner scDict = new Scanner(dict);
while (scDict.hasNext()) {//将词典按行写入列表
arrL.add(scDict.nextLine());
}
for (int n = 1; n < arrL.size(); n++) {//遍历列表
if (pt.matcher(arrL.get(n)).matches()) {//匹配变种单词行
String[] spl = arrL.get(n).split(", ");//对变种单词进行分词,写入临时数组
for (String string : spl) {//按<变种, 原型>写入Map
string = string.replaceAll("\\s{4}", "");
dictMap.put(string, arrL.get(n-1));
}
}
}
scDict.close();
return dictMap;//返回Map字典
}
}
3. 翻译词典初始化
这个是个画蛇添足的东西,但还是保留了。最初被来想用Google或YouDao的翻译API,看了一眼Google的已经收费了(部分貌似免费),YouDao的我也懒申请APIkey,就直接再次采用穷举的暴力方法。
package org.xinyo.GetFreq;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TransDict {
static HashMap<String, String> transRead(String tdPath) throws IOException{
String dict = ReadFile.read(tdPath,"utf-8");//按utf-8编码读取文本
ArrayList<String> arrL = new ArrayList<>();
HashMap<String, String> transDictMap = new HashMap<>();
Scanner scDict = new Scanner(dict);
while (scDict.hasNext()) {//将词典按行写入列表
arrL.add(scDict.nextLine());
}
for (String string : arrL) {//将翻译写入Map
Matcher m = Pattern.compile("^(.+)\\s{6}((.)+)").matcher(string);
while(m.find()){
transDictMap.put(m.group(1), m.group(2));
}
}
scDict.close();
return transDictMap;
}
}
4. 目标文本读入与解析
"(\\d|\\r|\\n)"
> ” ” //用空格替换数字和换行符"(\\p{Punct}|\\s){2,}"
> ” ” //用空格替换所有标点符号"(\\w+)'\\w+"
> “$1” //舍去撇号缩写单词后半部分
这里就是先正则表达式处理,然后按词典匹配变种,按<单词, 词频 >写入HashMap
package org.xinyo.GetFreq;
import java.util.HashMap;
import java.util.Scanner;
import java.util.regex.Pattern;
public class ParseFile {
public static HashMap<String, Integer> getFreq(String inPut, HashMap<String, String> dictMap){
HashMap<String, Integer> mp = new HashMap<String, Integer>();//key:单词,value:词频
Pattern regex1 = Pattern.compile("(\\d|\\r|\\n)");//空格替换数字和换行
Pattern regex2 = Pattern.compile("(\\p{Punct}|\\s){2,}");//空格替换标点
Pattern regex3 = Pattern.compile("(\\w+)'\\w+");//舍去上撇号之后内容
inPut = regex1.matcher(inPut).replaceAll(" ");
inPut = regex2.matcher(inPut).replaceAll(" ");
inPut = regex3.matcher(inPut).replaceAll("$1");
inPut = inPut.toLowerCase();//小写转换
Scanner scanner = new Scanner(inPut);
while(scanner.hasNext()){
String s = scanner.next();
if(dictMap.containsKey(s)){//按词典匹配,覆盖变种单词
s = dictMap.get(s);
}
if ((s.length()>1||s.equals("a")||s.equals("I"))||s.matches("(\\w|-|\\.)+")) {//纯字母单词或含-.单词
Integer freq = mp.get(s);
mp.put(s, freq == null ? 1 : freq + 1);
}
}
scanner.close();
return mp;
}
}
5. 按词频排序
按词频排序,也就是Map的Value排序,这里直接使用sort-a-mapkey-value-by-values-java 中的方案,将原方案的升序改为按值降序排序了。
package org.xinyo.GetFreq;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class SortMap{
public static <K, V extends Comparable<? super V>> Map<K, V> sort( Map<K, V> map) throws IOException {
List<Map.Entry<K, V>> list =
new LinkedList<Map.Entry<K, V>>( map.entrySet() );
Collections.sort( list, new Comparator<Map.Entry<K, V>>(){
public int compare( Map.Entry<K, V> o1, Map.Entry<K, V> o2 ){
return (o2.getValue()).compareTo( o1.getValue() );
}
} );
Map<K, V> result = new LinkedHashMap<K, V>();
for (Map.Entry<K, V> entry : list){
result.put( entry.getKey(), entry.getValue() ); //排序后的Map
}
return result;
}
}
6. 格式化输出
格式化输出主要是美观
package org.xinyo.GetFreq;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class OutPut {
static int tl = 0;
public static <K, V extends Comparable<? super V>> void fileWrite(Map<K, V> mapSorted, String pathout) throws IOException{
HashMap<String, String> transDictMap = TransDict.transRead("stardict1.3.txt");//读入翻译词典
for (K string : mapSorted.keySet()) {//统计总字数
tl += (int) mapSorted.get(string);
}
FileWriter writer = new FileWriter(pathout);
writer.write(" 总字数:" + tl + " 单词数:" + mapSorted.size() + " @xinyo.org\r\n\r\n");
String title = String.format( "%4s %-14s %6s %5s %-20s\r\n", "序号", "单词", "次数", "万分比", "翻译");
String title2 = String.format( "%6s %-16s %8s %8s %-20s\r\n", "----", "----", "----", "----", "----");
writer.write(title);
writer.write(title2);
String fm = new String();
int i = 0;
String trans = null;
for (Map.Entry<K, V> entry : mapSorted.entrySet()){//遍历排序后的Map
i++;
String key = (String) entry.getKey();
if(transDictMap.containsKey(key)){//匹配翻译词典
trans = transDictMap.get(key);
}else{
key = key.substring(0,1).toUpperCase()+key.substring(1);
if(transDictMap.containsKey(key)){
trans = transDictMap.get(key);
}else{
trans = "";
key = key.toLowerCase();
}
}
fm = String.format( "%6d %-16.16s %8d %8.8s %-20.35s \r\n", i, key, entry.getValue(), 10000*(int)(entry.getValue())/tl, trans);
writer.write(fm);
}
tl = 0;
writer.flush();
writer.close();
}
}
7. main()方法
注释掉的部分是对多个文件结果累加(mapPlusResult),统计多个文件的总词频
package org.xinyo.GetFreq;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
public class WordsCount {
public static void main(String[] args) throws IOException {
String[] p1 = {"theold.txt"};//统计目标文本,可以输入多个值
String[] p2 = {"000.txt"};//输出文本
Map<String, Integer> sortedMap = new LinkedHashMap<>();
// Map<String, Integer> mapPlusResult = new HashMap<>();
HashMap<String, String> dictMap = Dict.readDict("2+2+3lem.txt");//单词匹配词典
for (int i = 0; i < p1.length; i++) {
p2[i] = "000 " + p1[i];
String strIn = ReadFile.read(p1[i]);//读取文件
HashMap<String, Integer> map= ParseFile.getFreq(strIn, dictMap);//格式化文本,写入Map
sortedMap = SortMap.sort(map);//按值(词频)排序
OutPut.fileWrite(sortedMap, p2[i]);//输出排序后的Map到文本
// mapPlusResult = MapPlus.checkAndPlus(map, mapPlusResult);//对Map累加
}
// Map<String, Integer> plusSorted = SortMap.fMap(mapPlusResult);//对累加结果排序
// OutPut.fileWrite(plusSorted, "0.txt");
}
}
xy24012017 年 04 月 19 日上午 10:35
最近也想做类似的东西。有许多帮助。