什么是读取/过滤文本文件的最快方法?

3
我试图循环遍历一个文本日志文件,其中包含SSH登录和其他日志。
该程序返回SSH登录的总数。
我的解决方法可以工作,但速度有点慢(在200mo文件上需要约3.5秒)。我想知道是否有任何方法可以使它更快。我不太熟悉Java的最佳实践。
我正在使用BufferedReader类。也许有更好的类/方法,但我在网上找到的其他所有方法都比较慢。
{
            BufferedReader br;
            if(fileLocation != null) {
                br = new BufferedReader(new FileReader(fileLocation));
            }
            else {
                br = new BufferedReader((new InputStreamReader(System.in, "UTF-8")));
            }
            String line;
            Stack<String> users = new Stack<>();
            int succeeded = 0;
            int failed;
            int total = 0;

            if(!br.ready()) {
                help("Cannot read the file", true);
            }
            while((line=br.readLine())!=null)
            {
                if(!line.contains("sshd")) continue;
                String[] arr = line.split("\\s+");
                if(arr.length < 11) continue;


                String log = arr[4];
                String log2 = arr[5];
                String log3 = arr[8];
                String user = arr[10];
                if(!log.contains("sshd")) continue;
                if(!log2.contains("Accepted")) {
                    if(log3.contains("failure")) {
                        total++;
                    }
                    continue;
                }
                total++;
                succeeded++;

                if(!repeat) {
                    if (users.contains(user)) continue;
                    users.add(user);
                }

                System.out.println((total + 1) + " " + user);
            }

完整代码:https://pastebin.com/xp2P9wja

此外,以下是日志文件的部分内容:

Dec  3 12:20:12 k332 sshd[25206]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=10.147.222.137 
Dec  3 12:20:14 k332 sshd[25204]: error: PAM: Authentication failure for illegal user admin from 10.147.222.137
Dec  3 12:20:14 k332 sshd[25204]: Failed keyboard-interactive/pam for invalid user admin from 10.147.222.137 port 36417 ssh2
Dec  3 12:20:14 k332 sshd[25204]: Connection closed by invalid user admin 10.147.222.137 port 36417 [preauth]
Dec  3 12:20:40 k332 sshd[25209]: pam_tally2(sshd:auth): Tally overflowed for user root

最终输出为:
Total :
103 unique IP SSH logins succeeded
30387 SSH logins succeeded
17186 SSH logins failed
47573 total SSH logins

感谢您的时间!
编辑: Mo(Mega Octet)= MB(Mega Byte)(我们通常在法语中说Mo)
如果需要,这是完整的更新代码: https://pastebin.com/Kn5EqLNX

1
你能澄清一下“一个200兆文件”的意思吗? - Jon Skeet
以下是两个提高性能的提示,据我所知:if语句需要时间,您应该使用条件运算符(如&&组合它们。在控制台中打印也很耗时,您应该删除while循环中的System.out.println并等待进程结束,或者至少使用模数%每100或1000个条目打印一次。 - gui3
@gui3 我不是专家 嗯,我倾向于相信你,但你肯定可以猜测一下 PasswordAuthentication no 的意思以及它是否会倾向于加强或减弱安全性吧? - g00se
抱歉打扰这个帖子,但我需要提醒@g00se,讽刺在书面文本中很难理解,特别是当它不是你的母语时(这是我的情况和许多SO用户的情况),如果“PasswordAuthentication no”确实意味着软化您的安全性(此外还公布了IP),我相信您的评论可能会欺骗很多非专业程序员。没有冒犯之意 - 但我们在SO上是为了帮助或得到帮助,而不是为了开心。 - gui3
1
Stack 不够高效。请使用 HashSet 替代。(即使大部分 CPU 时间都不在这里。) - k314159
显示剩余4条评论
1个回答

6

如果您对代码进行分析,会发现问题在于String.split()方法:

进入图像描述

这是标准Java库中已知的问题:Java split String performances

因此,为了加速您的代码,您需要以某种方式加速代码的这一部分。我建议首先将第75-79行的代码替换为以下代码:

Pattern pattern = Pattern.compile("\\s+");
while ((line = br.readLine()) != null) {
    if (!line.contains("sshd")) continue;
    String[] arr = pattern.split(line);
    if (arr.length < 11) continue;
...
}

这可能会加快代码运行速度,但是从分析结果可以看出,很多时间仍然被用在Pattern和Matcher方法上。我们需要摆脱Pattern和Matcher以显著提高速度。

对于单个字符的模式,split函数可以在不使用正则表达式的情况下进行操作,并且效率很高。让我们尝试使用以下代码替换:

while ((line = br.readLine()) != null) {
    if (!line.contains("sshd")) continue;
    String[] arr = Arrays.stream(line.split(" "))
                    .filter(s -> !s.isEmpty())
                    .toArray(String[]::new);
    if (arr.length < 11) continue;
...
}

这段代码在相同的数据上运行速度几乎快了一倍。


太棒了!它确实将执行时间降低了一半,非常感谢!我现在会尝试使用配置文件来更好地理解如何优化我的代码。 - redlegamin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接