以相反的顺序逐行读取文件

2022-09-01 14:36:09

我有一个java ee应用程序,我使用servlet来打印使用log4j创建的日志文件。在读取日志文件时,您通常要查找最后一行日志,因此,如果 servlet 以相反的顺序打印日志文件,它将更有用。我的实际代码是:

    response.setContentType("text");
    PrintWriter out = response.getWriter();
    try {
        FileReader logReader = new FileReader("logfile.log");
        try {
            BufferedReader buffer = new BufferedReader(logReader);
            for (String line = buffer.readLine(); line != null; line = buffer.readLine()) {
                out.println(line);
            }
        } finally {
            logReader.close();
        }
    } finally {
        out.close();
    }

我在互联网上发现的实现涉及使用StringBuffer并在打印之前加载所有文件,难道没有一种代码轻量级的方式来寻找文件的末尾并读取内容直到文件开始吗?


答案 1

[编辑]

根据请求,我在此答案前面加上稍后评论的情绪:如果您经常需要此行为,“更合适”的解决方案可能是使用DBAppender将日志从文本文件移动到数据库表(log4j 2的一部分)。然后,您可以简单地查询最新条目。

[/编辑]

我可能会以与列出的答案略有不同的方式处理这个问题。

(1) 创建一个子类,该子类以相反的顺序写入每个字符的编码字节:Writer

public class ReverseOutputStreamWriter extends Writer {
    private OutputStream out;
    private Charset encoding;
    public ReverseOutputStreamWriter(OutputStream out, Charset encoding) {
        this.out = out;
        this.encoding = encoding;
    }
    public void write(int ch) throws IOException {
        byte[] buffer = this.encoding.encode(String.valueOf(ch)).array();
        // write the bytes in reverse order to this.out
    }
    // other overloaded methods
}

(2) 创建 log4j 的子类,其方法将被覆盖以创建 .WriterAppendercreateWriterReverseOutputStreamWriter

(3) 创建 log4j 的子类,其方法以相反的字符顺序返回日志字符串:Layoutformat

public class ReversePatternLayout extends PatternLayout {
    // constructors
    public String format(LoggingEvent event) {
        return new StringBuilder(super.format(event)).reverse().toString();
    }
}

(4) 修改我的日志记录配置文件,将日志消息同时发送到“正常”日志文件和“反向”日志文件。“反向”日志文件将包含与“普通”日志文件相同的日志消息,但每条消息都将向后写入。(请注意,“反向”日志文件的编码不一定符合 UTF-8,甚至不符合任何字符编码。

(5) 创建一个子类,该子类包装一个实例,以便以相反的顺序读取文件的字节:InputStreamRandomAccessFile

public class ReverseFileInputStream extends InputStream {
    private RandomAccessFile in;
    private byte[] buffer;
    // The index of the next byte to read.
    private int bufferIndex;
    public ReverseFileInputStream(File file) {
        this.in = new RandomAccessFile(File, "r");
        this.buffer = new byte[4096];
        this.bufferIndex = this.buffer.length;
        this.in.seek(file.length());
    }
    public void populateBuffer() throws IOException {
        // record the old position
        // seek to a new, previous position
        // read from the new position to the old position into the buffer
        // reverse the buffer
    }
    public int read() throws IOException {
        if (this.bufferIndex == this.buffer.length) {
            populateBuffer();
            if (this.bufferIndex == this.buffer.length) {
                return -1;
            }
        }
        return this.buffer[this.bufferIndex++];
    }
    // other overridden methods
}

现在,如果我想以相反的顺序读取“正常”日志文件的条目,我只需要创建一个实例,给它一个“revere”日志文件。ReverseFileInputStream


答案 2

这是一个古老的问题。我也想做同样的事情,经过一些搜索后发现apache commons-io中有一个类来实现这一点:

org.apache.commons.io.input.ReversedLinesFileReader