Java读取文件最后n行

时间:2025-03-18 09:50:08

目录

  • 代码由来
  • 实现过程
  • 代码

代码由来

项目里需要读取最新的日志,可是最新的日志都在文件的最后几行,所以有了这个需求。
百度之后先研究了别人的代码,感觉一是逻辑不太好理解且注释比较少,二是代码偏向于演示,会有bug,没有办法拿来就用。所以自己写了这个方法,希望可以给需要的同学带来帮助。
这个方法实现的功能和Linux系统的tail命令是相同的。

实现过程

对这个方法我前前后后投入了很大精力,每改变一次实现方式,前面的工作都仿佛白做了一样。从读取功能基本实现,到解决读取空行和文档行数不足导致的bug。再到费尽心机的简化代码,到最后一次实现方式的改变直接解决之前所有问题。到考虑性能把缓存工具的更换和代码执行逻辑的优化。
这个过程让我意识到:再简单的需求,用上复杂的实现就算把人累死也得不到好的结果。作为一名程序员,拿到需求我们往往急于写代码,当看到杂乱的实现代码,我们又抓耳挠腮无从下手去改进。每当这个时候,我们一定要回头去看看我们的需求,想想有没有其他更简洁的实现方法,而不要困死在已有的代码里。

感谢 @万物皆字节 同学的建议。

如果这篇博客帮助到了你,你可给它点个赞,这将是对我莫大的鼓励,谢谢!

代码

    /**
     * 读取文件最后几行 <br>
     * 相当于Linux系统中的tail命令 读取大小限制是2GB
     *
     * @param filename 文件名
     * @param charset  文件编码格式,传null默认使用defaultCharset
     * @param rows     读取行数
     * @throws IOException IOException
     */
    public static String readLastRows(String filename, Charset charset, int rows) throws IOException {
        charset = charset == null ? Charset.defaultCharset() : charset;
        byte[] lineSeparator = System.getProperty("").getBytes();
        try (RandomAccessFile rf = new RandomAccessFile(filename, "r")) {
            // 每次读取的字节数要和系统换行符大小一致
            byte[] c = new byte[lineSeparator.length];
            // 在获取到指定行数和读完文档之前,从文档末尾向前移动指针,遍历文档每一个字节
            for (long pointer = rf.length(), lineSeparatorNum = 0; pointer >= 0 && lineSeparatorNum < rows;) {
                // 移动指针
                rf.seek(pointer--);
                // 读取数据
                int readLength = rf.read(c);
                if (readLength != -1 && Arrays.equals(lineSeparator,c)) {
                    lineSeparatorNum++;
                }
                //扫描完依然没有找到足够的行数,将指针归0
                if (pointer == -1 && lineSeparatorNum < rows) {
                    rf.seek(0);
                }
            }
            byte[] tempbytes = new byte[(int) (rf.length() - rf.getFilePointer())];
            rf.readFully(tempbytes);
            return new String(tempbytes, charset);
        }
    }