用Java替换大型文本文件中所有特殊字符和数字的有效方法

时间:2016-11-28 16:07:21

标签: java regex performance text-files frequency

我目前正在开发一个根据文本文件中的字母频率创建饼图的程序,我的测试文件相对较大,虽然我的程序在较小的文件上工作得很好但对于大文件来说速度很慢。我想通过找出一种更有效的方式来搜索文本文件并删除特殊字符和数字来减少所需的时间。这是我现在对这部分的代码:

public class readFile extends JPanel {
protected static String stringOfChar = "";
    public static String openFile(){
    String s = "";
            try {
                BufferedReader reader = new BufferedReader(new FileReader("xWords.txt"));
                while((s = reader.readLine()) != null){
                    String newstr = s.replaceAll("[^a-z A-Z]"," ");
                    stringOfChar+=newstr;
                }
                reader.close();
                return stringOfChar;
            }
            catch (Exception e) {
                System.out.println("File not found.");
            }
            return stringOfChar;
    }

代码逐个字符地读取文本文件,用空格替换所有特殊字符,完成后我将字符串排序为字符和频率的散列图。

我从测试中知道这部分代码是导致大量额外时间处理文件的原因,但我不确定如何以有效的方式替换所有字符。

2 个答案:

答案 0 :(得分:3)

您的代码有两个效率低下:

  • 它构造了丢弃的字符串,其中特殊字符被s.replaceAll
  • 中的空格替换
  • 它通过将String个对象与+=
  • 连接来构建大字符串

这两个操作都会产生许多不必要的对象。最重要的是,最终String对象被丢弃,并且最终结果即计数图被构建。

您应该能够通过在阅读文件时构建地图来解决这两个缺陷,同时避免替换和连接:

public static Map<Character,Integer> openFileAndCount() {
    Map<Character,Integer> res = new HashMap<Character,Integer>();
    BufferedReader reader = new BufferedReader(new FileReader("xWords.txt"));
    String s;
    while((s = reader.readLine()) != null) {
        for (int i = 0 ; i != s.length() ; i++) {
            char c = s.charAt(i);
            // The check below lets through all letters, not only Latin ones.
            // Use a different check to get rid of accented letters
            // e.g. è, à, ì and other characters that you do not want.
            if (!Character.isLetter(c)) {
                c = ' ';
            }
            res.put(c, res.containsKey(c) ? res.get(c).intValue()+1 : 1);
        }
    }
    return res;
}

答案 1 :(得分:0)

而不是使用运算符+使用类StringBuilder来连接字符串:

  

可变的字符序列。

效率更高。

连接字符串为每个连接生成一个新字符串。因此,如果您需要多次这样做,那么对于从未使用过的中间字符串有很多字符串创建,因为您只需要最终结果。

StringBuilder使用不同的内部表示,因此不必为每个连接创建新对象。

replaceAll每次创建新的String效率非常高。

这是使用StringBuilder的更有效的代码:

...
StringBuilder build = new StringBuilder();
while((s = reader.readLine()) != null){
    for (char ch : s) {
        if (!(ch >= 'a' && ch <= 'z') 
              && !(ch >= 'A' && ch <= 'Z')
              && ch != ' ') {
            build.append(" ");
        } else {
            build.append(ch);
        }
    }
}
... 
return build.toString();
...