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

Efficient way to replace all special characters and numbers in a large text file in Java

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

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;
    }

代码逐字符读取文本文件,用 space 替换所有特殊字符,完成后,我将字符串排序为字符和频率的哈希映射。

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

您的代码有两个低效之处:

  • s.replaceAll
  • 中用space替换特殊字符构造一次性字符串
  • 它通过将 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;
}

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

A mutable sequence of characters.

效率高了很多。

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

A 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();
...