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

在Java中替换大型文本文件中所有特殊字符和数字的有效方法,java,regex,performance,text-files,frequency,Java,Regex,Performance,Text Files,Frequency,我目前正在开发一个基于文本文件中字母频率创建饼图的程序,我的测试文件相对较大,尽管我的程序在较小的文件上运行得很好,但在较大的文件上运行速度非常慢。我想找出一种更有效的方法来搜索文本文件并删除特殊字符和数字,从而减少所需的时间。这是我现在为这一部分编写的代码: public class readFile extends JPanel { protected static String stringOfChar = ""; public static String openFile(){

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

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;
    }
代码逐字符读取文本文件,用空格替换所有特殊字符,完成后,我将字符串排序为字符和频率的哈希映射


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

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

  • 它在
    s.replaceAll
    s.replaceAll中用空格替换特殊字符来构造丢弃字符串
  • 它通过将
    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;
}
public静态映射openFileAndCount(){
Map res=新的HashMap();
BufferedReader=新的BufferedReader(新文件阅读器(“xWords.txt”);
字符串s;
而((s=reader.readLine())!=null){
对于(int i=0;i!=s.length();i++){
char c=s.charAt(i);
//下面的检查允许通过所有字母,而不仅仅是拉丁字母。
//使用不同的检查来消除重音字母
//例如è、ì、ì和其他您不想要的字符。
if(!Character.isleter(c)){
c='';
}
res.put(c,res.containsKey(c)→res.get(c).intValue()+1:1);
}
}
返回res;
}

不要使用运算符+使用类连接字符串:

可变的字符序列

它的效率要高得多

串联字符串为每个串联生成一个新字符串。因此,如果您多次需要这样做,您将有大量的中间字符串创建,这些字符串从未使用过,因为您只需要最终结果

StringBuilder
使用不同的内部表示法,因此无需为每次连接创建新对象

另外,
replaceAll
每次都创建一个新的
字符串
是非常低效的

下面是一个使用
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();
...
。。。
StringBuilder build=新建StringBuilder();
而((s=reader.readLine())!=null){
用于(字符ch:s){

如果(!(ch>='a'&&ch='a'&&ch='a'&&ch请遵循命名约定。
stringOfChar+=newstr;
不要通过在循环中将新部分连接到结果字符串来构建结果字符串。请使用
StringBuilder
及其
append
方法(更多信息-或可能的副本:)。如果要添加分隔符,可以使用
StringJoiner
及其
add
方法。事实上,根本不要生成字符串结果,因为只需将其再次拆分为字符。将字符直接输入计数机制即可。(请不要将文件读取逻辑放在
JPanel
中。将UI与业务逻辑分离虽然不费吹灰之力,但非常值得。)StringBuilder解决了这个问题,但我同意这个实现绝对避免了不必要的操作!这是我第一个实现任何类型的映射的项目,所以我没有想到这一点!非常感谢!!Character.isleter也为特殊字母返回true,如è、è、ì等。因此不能用作常规e的等价物xpression[a-z a-z]@davidorenzomarino这是一个公平的观察,我添加了一条评论,提到一些可能不需要的字符可能会通过。谢谢!如果您需要更改op的代码以返回不同的数据结构,可能int数组比Map更好。您可以返回27的数组(26个字母+1个空格)或53个(26个大写字母、26个小写字母和1个空格)整数。您不需要任何映射开销,代码也不需要检索和三元operator@DavideLorenzoMARINO这也是事实。我选择了哈希映射,因为OP提到他转换字符串“转换为字符和频率的hashmap”,但考虑到他的限制,53个整数的简单数组将更加有效。StringBuilder使它运行得非常漂亮!感谢您的推荐!!