在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使它运行得非常漂亮!感谢您的推荐!!