Java 按相反顺序逐行读取文件

Java 按相反顺序逐行读取文件,java,file,text,log4j,Java,File,Text,Log4j,我有一个JavaEE应用程序,其中我使用servlet打印使用log4j创建的日志文件。在读取日志文件时,您通常会查找最后一行日志,因此,如果servlet以相反的顺序打印日志文件,它将非常有用。我的实际代码是: response.setContentType("text"); PrintWriter out = response.getWriter(); try { FileReader logReader = new FileReader("logfi

我有一个JavaEE应用程序,其中我使用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并在打印之前加载所有文件,难道没有一种代码简单的方法来查找文件末尾并读取内容直到文件开头吗?

我认为使用类是一个很好的选择。有一些使用此类进行回读的示例代码。以这种方式读取字节很容易,但是读取字符串可能更具挑战性。

好问题。我不知道这有什么常见的实现。做得好也不是小事,所以要小心你的选择。它应该处理字符集编码和检测不同的换行方法。这是我到目前为止使用ASCII和UTF-8编码文件的实现,包括UTF-8的测试用例。它不适用于UTF-16LE或UTF-16BE编码文件

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import junit.framework.TestCase;

public class ReverseLineReader {
    private static final int BUFFER_SIZE = 8192;

    private final FileChannel channel;
    private final String encoding;
    private long filePos;
    private ByteBuffer buf;
    private int bufPos;
    private byte lastLineBreak = '\n';
    private ByteArrayOutputStream baos = new ByteArrayOutputStream();

    public ReverseLineReader(File file, String encoding) throws IOException {
        RandomAccessFile raf = new RandomAccessFile(file, "r");
        channel = raf.getChannel();
        filePos = raf.length();
        this.encoding = encoding;
    }

    public String readLine() throws IOException {
        while (true) {
            if (bufPos < 0) {
                if (filePos == 0) {
                    if (baos == null) {
                        return null;
                    }
                    String line = bufToString();
                    baos = null;
                    return line;
                }

                long start = Math.max(filePos - BUFFER_SIZE, 0);
                long end = filePos;
                long len = end - start;

                buf = channel.map(FileChannel.MapMode.READ_ONLY, start, len);
                bufPos = (int) len;
                filePos = start;
            }

            while (bufPos-- > 0) {
                byte c = buf.get(bufPos);
                if (c == '\r' || c == '\n') {
                    if (c != lastLineBreak) {
                        lastLineBreak = c;
                        continue;
                    }
                    lastLineBreak = c;
                    return bufToString();
                }
                baos.write(c);
            }
        }
    }

    private String bufToString() throws UnsupportedEncodingException {
        if (baos.size() == 0) {
            return "";
        }

        byte[] bytes = baos.toByteArray();
        for (int i = 0; i < bytes.length / 2; i++) {
            byte t = bytes[i];
            bytes[i] = bytes[bytes.length - i - 1];
            bytes[bytes.length - i - 1] = t;
        }

        baos.reset();

        return new String(bytes, encoding);
    }

    public static void main(String[] args) throws IOException {
        File file = new File("my.log");
        ReverseLineReader reader = new ReverseLineReader(file, "UTF-8");
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }
    }

    public static class ReverseLineReaderTest extends TestCase {
        public void test() throws IOException {
            File file = new File("utf8test.log");
            String encoding = "UTF-8";

            FileInputStream fileIn = new FileInputStream(file);
            Reader fileReader = new InputStreamReader(fileIn, encoding);
            BufferedReader bufReader = new BufferedReader(fileReader);
            List<String> lines = new ArrayList<String>();
            String line;
            while ((line = bufReader.readLine()) != null) {
                lines.add(line);
            }
            Collections.reverse(lines);

            ReverseLineReader reader = new ReverseLineReader(file, encoding);
            int pos = 0;
            while ((line = reader.readLine()) != null) {
                assertEquals(lines.get(pos++), line);
            }

            assertEquals(lines.size(), pos);
        }
    }
}
导入java.io.BufferedReader;
导入java.io.ByteArrayOutputStream;
导入java.io.File;
导入java.io.FileInputStream;
导入java.io.IOException;
导入java.io.InputStreamReader;
导入java.io.RandomAccessFile;
导入java.io.Reader;
导入java.io.UnsupportedEncodingException;
导入java.nio.ByteBuffer;
导入java.nio.channels.FileChannel;
导入java.util.ArrayList;
导入java.util.Collections;
导入java.util.List;
导入junit.framework.TestCase;
公共类反向读取器{
私有静态最终整数缓冲区大小=8192;
专用最终文件通道;
私有最终字符串编码;
私人长文件POS;
私人ByteBuffer buf;
私立国际学校;
专用字节lastLineBreak='\n';
private ByteArrayOutputStream bas=new ByteArrayOutputStream();
公共ReverseLineReader(文件、字符串编码)引发IOException{
RandomAccessFile raf=新的RandomAccessFile(文件“r”);
channel=raf.getChannel();
filePos=raf.length();
this.encoding=编码;
}
公共字符串readLine()引发IOException{
while(true){
如果(bufPos<0){
如果(filePos==0){
if(baos==null){
返回null;
}
字符串行=bufToString();
baos=null;
回流线;
}
long start=Math.max(filePos-缓冲区大小,0);
长端=filePos;
长透镜=结束-开始;
buf=channel.map(FileChannel.MapMode.READ_ONLY,start,len);
bufPos=(int)len;
filePos=开始;
}
而(bufPos-->0){
字节c=buf.get(bufPos);
如果(c='\r'| c=='\n'){
如果(c!=lastLineBreak){
lastLineBreak=c;
继续;
}
lastLineBreak=c;
返回bufToString();
}
编写(c);
}
}
}
私有字符串bufToString()引发不支持的DencodingException{
如果(bas.size()==0){
返回“”;
}
byte[]bytes=baos.toByteArray();
对于(int i=0;i
[编辑]

根据请求,我在回答这个问题之前加入了后面的评论:如果您经常需要这种行为,“更合适”的解决方案可能是使用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
}
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
}
(2) 创建log4j
WriterAppender
的子类,其
createWriter
方法将被重写以创建
reverseoutStreamWriter
的实例

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

public class ReversePatternLayout extends PatternLayout {
    // constructors
    public String format(LoggingEvent event) {
        return new StringBuilder(super.format(event)).reverse().toString();
    }
}
(4) 修改我的日志配置文件,将日志消息发送到“正常”日志文件和“反向”日志文件
FileInputStream fis = // .. logfile
BufferedInputStream bis = new BufferedInputStream(fis);
CountingInputStream cis = new CountingInputSteam(bis);
InputStreamReader isr = new InputStreamReader(cis, "UTF-8");
new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec("tail yourlogfile.txt -n 50 | rev").getProcess().getInputStream()))
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

import com.google.common.io.LineProcessor;
public class FileUtils {
/**
 * 反向读取文本文件(UTF8),文本文件分行是通过\r\n
 * 
 * @param <T>
 * @param file
 * @param step 反向寻找的步长
 * @param lineprocessor
 * @throws IOException
 */
public static <T> T backWardsRead(File file, int step,
        LineProcessor<T> lineprocessor) throws IOException {
    RandomAccessFile rf = new RandomAccessFile(file, "r");
    long fileLen = rf.length();
    long pos = fileLen - step;
    // 寻找倒序的第一行:\r
    while (true) {
        if (pos < 0) {
            // 处理第一行
            rf.seek(0);
            lineprocessor.processLine(rf.readLine());
            return lineprocessor.getResult();
        }
        rf.seek(pos);
        char c = (char) rf.readByte();
        while (c != '\r') {
            c = (char) rf.readByte();
        }
        rf.readByte();//read '\n'
        pos = rf.getFilePointer();
        if (!lineprocessor.processLine(rf.readLine())) {
            return lineprocessor.getResult();
        }
        pos -= step;
    }

  }
       FileUtils.backWardsRead(new File("H:/usersfavs.csv"), 40,
            new LineProcessor<Void>() {
                                   //TODO  implements method
                                   .......
            });
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
 * Inside of C:\\temp\\vaquar.txt we have following content
 * vaquar khan is working into Citi He is good good programmer programmer trust me
 * @author vaquar.khan@gmail.com
 *
 */

public class ReadFileAndDisplayResultsinReverse {
    public static void main(String[] args) {
        try {
            // read data from file
            Object[] wordList = ReadFile();
            System.out.println("File data=" + wordList);
            //
            Set<String> uniquWordList = null;
            for (Object text : wordList) {
                System.out.println((String) text);
                List<String> tokens = Arrays.asList(text.toString().split("\\s+"));
                System.out.println("tokens" + tokens);
                uniquWordList = new HashSet<String>(tokens);
                // If multiple line then code into same loop
            }
            System.out.println("uniquWordList" + uniquWordList);

            Comparator<String> wordComp= new Comparator<String>() {

                @Override
                public int compare(String o1, String o2) {
                    if(o1==null && o2 ==null) return 0;
                    if(o1==null ) return o2.length()-0;
                    if(o2 ==null) return o1.length()-0;
                    //
                    return o2.length()-o1.length();
                }
            };
            List<String> fs=new ArrayList<String>(uniquWordList);
            Collections.sort(fs,wordComp);

            System.out.println("uniquWordList" + fs);

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    static Object[] ReadFile() throws IOException {
        List<String> list = Files.readAllLines(new File("C:\\temp\\vaquar.txt").toPath(), Charset.defaultCharset());
        return list.toArray();
    }


}
try (Stream<String> logStream = Files.lines(Paths.get("C:\\logfile.log"))) {
   logStream
      .sorted(Comparator.reverseOrder())
      .limit(10) // last 10 lines
      .forEach(System.out::println);
}