分析一个文本文件中各个词出现的频率

要求:

  写一个程序,分析一个文本文件中各个词出现的频率,并且把频率最高的10个词打印出来。文本文件大约是30KB~300KB大小。
 

解决步骤:

  1、读取一个 txt 文本文件;
  2、统计文件里面每个词出现的次数;
  3、进行排序,打印出频率最高的10个词。
 
编程语言:java;
测试文本:D:\wordtest.txt         大小:417 KB (427,605 字节)
性能测试工具:JDK自带的 VisualVM插件

初步思路:

  1、将文件内容存放在 StringBuffer 里面;
  2、利用 split() 函数分割字符串,按照 ","、"."、"?"、":"、"空格"、"回车" 来分割,得到一个数组;
  3、遍历数组,将其放入 Map<String,Integer> 中,key=词,value=出现次数;
  4、对Map 进行排序,得到出现频率最高的10个词;

程序实现:

 1 package homework;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.FileNotFoundException;
 5 import java.io.FileReader;
 6 import java.io.IOException;
 7 import java.util.HashMap;
 8 import java.util.Iterator;
 9 import java.util.Map;
10 import java.util.Set;
11 import java.util.StringTokenizer;
12 import java.util.TreeSet;
13 
14 public class FileWordsCount {
15     public static void main(String[] args) {
16         
17         long time1 = System.currentTimeMillis();
18         
19         try {
20             BufferedReader br = new BufferedReader(new FileReader("D:\\wordtest.txt"));
21             String s;
22             StringBuffer sb = new StringBuffer();
23             while((s = br.readLine())!= null){
24                 sb.append(s);
25             }
26             Map<String,Integer> map = new HashMap<String,Integer>();
27             StringTokenizer st = new StringTokenizer(sb.toString(), ",.! ?\n");
28             while(st.hasMoreTokens()){
29                 String letter = st.nextToken();
30                 int count;
31                 if(map.get(letter) == null){
32                     count = 1;
33                 }else{
34                     count = map.get(letter).intValue()+1;
35                 }
36                 map.put(letter, count);
37             }
38             Set<WordEntity> set = new TreeSet<WordEntity>();
39             for(String key:map.keySet()){
40                 set.add(new WordEntity(key, map.get(key)));
41             }
42             /*System.out.print("单词        次数\n");
43             for(Iterator<WordEntity> it = set.iterator();it.hasNext();){
44                 WordEntity w = it.next();
45                 System.out.println(w.getKey()+"    :"+w.getCount());
46             }*/
47     
48             int count = 1;
49             for(Iterator<WordEntity> it = set.iterator();it.hasNext();){
50                 WordEntity w = it.next();
51                 System.out.println("Top"+count+": "+w.getKey()+"     次数:"+w.getCount());
52                 if(count == 10){
53                     break;
54                 }
55                 count++;
56             }
57         } catch (FileNotFoundException e) {
58             System.out.println("文件未找到!");
59         } catch (IOException e){
60             System.out.println("文件读异常!");
61         }
62         long time2 = System.currentTimeMillis();
63         System.out.println("耗时:");
64         System.out.println(time2 - time1+"ms");
65     }
66 }
 1 package homework;
 2 
 3 public class WordEntity implements Comparable<WordEntity>{
 4     
 5     public WordEntity(String key,Integer count){
 6         this.key = key;
 7         this.count = count;
 8     }
 9 
10     public String getKey(){
11         return key;
12     }
13     
14     public Integer getCount(){
15         return count;
16     }
17     
18     @Override
19     //cmp升序。-cmp降序排列  TreeSet会调用WorkForMap的compareTo方法来决定自己的排序
20     public int compareTo(WordEntity o) {
21         int cmp = count.intValue() - o.count.intValue();
22         return (cmp == 0 ? key.compareTo(o.key):-cmp); 
23     }
24     
25     @Override
26     public String toString() {    
27         return key + "出现次数:" + count;
28     }
29     
30     private String key;
31     private Integer count;
32     
33 }

 

运行结果:

 
 
 
 
 
 
 
 
 
 
 
 
 

 

不足与改进:

  单词分割没有考虑全面,比如"_"和"—",还有"单双引号",省略号等,字母的大小写等;改进程序,用正则表达式来匹配单词,存储在TreeMap,要按照TreeMap的value排序,默认是key排序,可以将Map.Entry放在集合里,重写比较器,再用 Collections.sort(list,comparator) 进行排序。
 

改进代码:

 1 package homework;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.FileReader;
 5 import java.util.ArrayList;
 6 import java.util.Collections;
 7 import java.util.Comparator;
 8 import java.util.List;
 9 import java.util.Map;
10 import java.util.TreeMap;
11 import java.util.regex.Matcher;
12 import java.util.regex.Pattern;
13 
14 public class WordCount {
15     public static void main(String[] args) throws Exception {
16         
17         long time1 = System.currentTimeMillis();
18 
19         BufferedReader reader = new BufferedReader(new FileReader(
20                 "D:\\wordtest.txt"));
21         StringBuffer buffer = new StringBuffer();
22         String line = null;
23         while ((line = reader.readLine()) != null) {
24             buffer.append(line);
25         }
26         reader.close();
27         Pattern expression = Pattern.compile("[a-zA-Z]+");// 定义正则表达式匹配单词
28         String string = buffer.toString();
29         Matcher matcher = expression.matcher(string);//
30         Map<String, Integer> map = new TreeMap<String, Integer>();
31         String word = "";
32         int times = 0;
33         while (matcher.find()) {// 是否匹配单词
34             word = matcher.group();// 得到一个单词-树映射的键
35             if (map.containsKey(word)) {// 如果包含该键,单词出现过
36                 times = map.get(word);// 得到单词出现的次数
37                 map.put(word, times + 1);
38             } else {
39                 map.put(word, 1);// 否则单词第一次出现,添加到映射中
40             }
41         }
42         /*
43          * 核心:如何按照TreeMap 的value排序而不是key排序.将Map.Entry放在集合里,重写比较器,在用
44          * Collections.sort(list, comparator);进行排序
45          */
46 
47         List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(
48                 map.entrySet());
49         /*
50          * 重写比较器
51          * 取出单词个数(value)比较
52          */
53         Comparator<Map.Entry<String, Integer>> comparator = new Comparator<Map.Entry<String, Integer>>() {
54             public int compare(Map.Entry<String, Integer> left,
55                     Map.Entry<String, Integer> right) {
56                 return (left.getValue()).compareTo(right.getValue());
57             }
58         };
59         Collections.sort(list, comparator);// 排序
60         // 打印
61         int last = list.size() - 1;
62         for (int i = last; i > last-10; i--) {
63             String key = list.get(i).getKey();
64             Integer value = list.get(i).getValue();
65             System.out.print("Top"+i+"  : ");
66             System.out.println(key + " " + value);
67         }
68         long time2 = System.currentTimeMillis();
69         System.out.println("耗时:");
70         System.out.println(time2 - time1+"ms");
71     }
72 }

 

运行结果:

结果对比:

  程序1 和程序2 结果前9名相似,最后一个虽然不一样,但是出现次数一样,为什么不一样呢?因为程序一里面TreeMap排序是按照key排序,所以虽然次数相同,但是 play 比 was 排在前面。

 

性能测试:

  1. 整体:

图1:程序运行前(初始状态)
程序运行前(初始状态)
 
 
 
 
图2:程序1 (改进前)测试情况
 
 
 
 
图3:程序2(改进后)测试情况
 
 
 

  2. CUP & GC

 

 

 

  3. Heap

程序1

  

 

  程序2

 

 

 

  4. Threads

程序1

 

 

程序2

 

 

 

  5. Class

程序1

 

 

 

程序2

  
 

改进与扩展:

  对于处理大数据采用拆分的方法,比较好。使用Java正则表达式,如果文章比较大,会造成栈溢出,因为测试文本在500KB以下,所以没有溢出。但是如果测试很大的文本,就不行。除此之外,还可以进行扩展,可以将正则表达式改变,设计一个对中英文混合,或者中文文件查找算法及程序实现,同时考虑读取存储方式的改进,提高效率和性能。
 
posted @ 2014-03-17 08:28  洋槐树  阅读(3284)  评论(4编辑  收藏  举报