如何在Java中从头开始读取文件(以相反顺序)?

我想从结尾到开始我的文件反方向读取文件,

[1322110800] LOG ROTATION: DAILY [1322110800] LOG VERSION: 2.0 [1322110800] CURRENT HOST STATE:arsalan.hussain;DOWN;HARD;1;CRITICAL - Host Unreachable (192.168.1.107) [1322110800] CURRENT HOST STATE: localhost;UP;HARD;1;PING OK - Packet loss = 0%, RTA = 0.06 ms [1322110800] CURRENT HOST STATE: musewerx-72c7b0;UP;HARD;1;PING OK - Packet loss = 0%, RTA = 0.27 ms 

我用这种方式读取代码,

 String strpath="/var/nagios.log"; FileReader fr = new FileReader(strpath); BufferedReader br = new BufferedReader(fr); String ch; int time=0; String Conversion=""; do { ch = br.readLine(); out.print(ch+"<br/>"); } while (ch != null); fr.close(); 

我宁愿使用缓冲器阅读器以相反的顺序阅读

据我所知,你尝试逐行倒退。 假设这是你试图阅读的文件:

一号线
2号线
3号线

并且你想把它写到servlet的输出stream如下:

3号线
2号线
一号线

以下代码在这种情况下可能会有所帮助:

  List<String> tmp = new ArrayList<String>(); do { ch = br.readLine(); tmp.add(ch); out.print(ch+"<br/>"); } while (ch != null); for(int i=tmp.size()-1;i>=0;i--) { out.print(tmp.get(i)+"<br/>"); } 

我遇到了与此处所述相同的问题。 我想从相反的顺序看文件中的行,从最后回到开始(unix tac命令将执行此操作)。

然而,我的input文件相当大,所以把整个文件读入内存,就像其他的例子对我来说不是一个可行的选项。

下面是我想到的类,它使用RandomAccessFile ,但不需要任何缓冲区,因为它只是保留指向文件本身的指针,并且与标准的InputStream方法一起工作。

它适用于我的案件,空文件和我试过的其他一些东西。 现在我没有Unicode字符或任何奇特的东西,但只要行被LF分隔,即使他们有一个LF + CR它应该工作。

基本用法是:

 in = new BufferedReader (new InputStreamReader (new ReverseLineInputStream(file))); while(true) { String line = in.readLine(); if (line == null) { break; } System.out.println("X:" + line); } 

这里是主要来源:

 package www.kosoft.util; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.RandomAccessFile; public class ReverseLineInputStream extends InputStream { RandomAccessFile in; long currentLineStart = -1; long currentLineEnd = -1; long currentPos = -1; long lastPosInFile = -1; public ReverseLineInputStream(File file) throws FileNotFoundException { in = new RandomAccessFile(file, "r"); currentLineStart = file.length(); currentLineEnd = file.length(); lastPosInFile = file.length() -1; currentPos = currentLineEnd; } public void findPrevLine() throws IOException { currentLineEnd = currentLineStart; // There are no more lines, since we are at the beginning of the file and no lines. if (currentLineEnd == 0) { currentLineEnd = -1; currentLineStart = -1; currentPos = -1; return; } long filePointer = currentLineStart -1; while ( true) { filePointer--; // we are at start of file so this is the first line in the file. if (filePointer < 0) { break; } in.seek(filePointer); int readByte = in.readByte(); // We ignore last LF in file. search back to find the previous LF. if (readByte == 0xA && filePointer != lastPosInFile ) { break; } } // we want to start at pointer +1 so we are after the LF we found or at 0 the start of the file. currentLineStart = filePointer + 1; currentPos = currentLineStart; } public int read() throws IOException { if (currentPos < currentLineEnd ) { in.seek(currentPos++); int readByte = in.readByte(); return readByte; } else if (currentPos < 0) { return -1; } else { findPrevLine(); return read(); } } } 

上面贴出的ReverseLineInputStream正是我所期待的。 我正在阅读的文件很大,无法缓冲。

有几个错误:

  • 文件未closures
  • 如果最后一行没有结束,则在第一次读取时返回最后两行。

这里是更正的代码:

 package www.kosoft.util; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; public class ReverseLineInputStream extends InputStream { RandomAccessFile in; long currentLineStart = -1; long currentLineEnd = -1; long currentPos = -1; long lastPosInFile = -1; int lastChar = -1; public ReverseLineInputStream(File file) throws FileNotFoundException { in = new RandomAccessFile(file, "r"); currentLineStart = file.length(); currentLineEnd = file.length(); lastPosInFile = file.length() -1; currentPos = currentLineEnd; } private void findPrevLine() throws IOException { if (lastChar == -1) { in.seek(lastPosInFile); lastChar = in.readByte(); } currentLineEnd = currentLineStart; // There are no more lines, since we are at the beginning of the file and no lines. if (currentLineEnd == 0) { currentLineEnd = -1; currentLineStart = -1; currentPos = -1; return; } long filePointer = currentLineStart -1; while ( true) { filePointer--; // we are at start of file so this is the first line in the file. if (filePointer < 0) { break; } in.seek(filePointer); int readByte = in.readByte(); // We ignore last LF in file. search back to find the previous LF. if (readByte == 0xA && filePointer != lastPosInFile ) { break; } } // we want to start at pointer +1 so we are after the LF we found or at 0 the start of the file. currentLineStart = filePointer + 1; currentPos = currentLineStart; } public int read() throws IOException { if (currentPos < currentLineEnd ) { in.seek(currentPos++); int readByte = in.readByte(); return readByte; } else if (currentPos > lastPosInFile && currentLineStart < currentLineEnd) { // last line in file (first returned) findPrevLine(); if (lastChar != '\n' && lastChar != '\r') { // last line is not terminated return '\n'; } else { return read(); } } else if (currentPos < 0) { return -1; } else { findPrevLine(); return read(); } } @Override public void close() throws IOException { if (in != null) { in.close(); in = null; } } } 

Apache Commons IO现在拥有ReversedLinesFileReader类(从2.2版开始)。

所以你的代码可能是:

 String strpath="/var/nagios.log"; ReversedLinesFileReader fr = new ReversedLinesFileReader(new File(strpath)); String ch; int time=0; String Conversion=""; do { ch = fr.readLine(); out.print(ch+"<br/>"); } while (ch != null); fr.close(); 

当您尝试读取数千行时,build议的ReverseLineInputStream工作非常 。 在我的个人电脑英特尔酷睿i7固态硬盘驱动器是在80秒约60k线。 下面是带有缓冲读取的灵感优化版本(与ReverseLineInputStream中的一次一字节读取相反)。 在400毫秒内读取60k行日志文件:

 public class FastReverseLineInputStream extends InputStream { private static final int MAX_LINE_BYTES = 1024 * 1024; private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024; private RandomAccessFile in; private long currentFilePos; private int bufferSize; private byte[] buffer; private int currentBufferPos; private int maxLineBytes; private byte[] currentLine; private int currentLineWritePos = 0; private int currentLineReadPos = 0; private boolean lineBuffered = false; public ReverseLineInputStream(File file) throws IOException { this(file, DEFAULT_BUFFER_SIZE, MAX_LINE_BYTES); } public ReverseLineInputStream(File file, int bufferSize, int maxLineBytes) throws IOException { this.maxLineBytes = maxLineBytes; in = new RandomAccessFile(file, "r"); currentFilePos = file.length() - 1; in.seek(currentFilePos); if (in.readByte() == 0xA) { currentFilePos--; } currentLine = new byte[maxLineBytes]; currentLine[0] = 0xA; this.bufferSize = bufferSize; buffer = new byte[bufferSize]; fillBuffer(); fillLineBuffer(); } @Override public int read() throws IOException { if (currentFilePos <= 0 && currentBufferPos < 0 && currentLineReadPos < 0) { return -1; } if (!lineBuffered) { fillLineBuffer(); } if (lineBuffered) { if (currentLineReadPos == 0) { lineBuffered = false; } return currentLine[currentLineReadPos--]; } return 0; } private void fillBuffer() throws IOException { if (currentFilePos < 0) { return; } if (currentFilePos < bufferSize) { in.seek(0); in.read(buffer); currentBufferPos = (int) currentFilePos; currentFilePos = -1; } else { in.seek(currentFilePos); in.read(buffer); currentBufferPos = bufferSize - 1; currentFilePos = currentFilePos - bufferSize; } } private void fillLineBuffer() throws IOException { currentLineWritePos = 1; while (true) { // we've read all the buffer - need to fill it again if (currentBufferPos < 0) { fillBuffer(); // nothing was buffered - we reached the beginning of a file if (currentBufferPos < 0) { currentLineReadPos = currentLineWritePos - 1; lineBuffered = true; return; } } byte b = buffer[currentBufferPos--]; // \n is found - line fully buffered if (b == 0xA) { currentLineReadPos = currentLineWritePos - 1; lineBuffered = true; break; // just ignore \r for now } else if (b == 0xD) { continue; } else { if (currentLineWritePos == maxLineBytes) { throw new IOException("file has a line exceeding " + maxLineBytes + " bytes; use constructor to pickup bigger line buffer"); } // write the current line bytes in reverse order - reading from // the end will produce the correct line currentLine[currentLineWritePos++] = b; } } }} 
 @Test public void readAndPrintInReverseOrder() throws IOException { String path = "src/misctests/test.txt"; BufferedReader br = null; try { br = new BufferedReader(new FileReader(path)); Stack<String> lines = new Stack<String>(); String line = br.readLine(); while(line != null) { lines.push(line); line = br.readLine(); } while(! lines.empty()) { System.out.println(lines.pop()); } } finally { if(br != null) { try { br.close(); } catch(IOException e) { // can't help it } } } } 

请注意,此代码读取内存中的空洞文件,然后开始打印它。 这是您可以使用缓冲读取器或其他不支持查找的读取器的唯一方法。 你必须记住这一点,在你的情况下,你想读取一个日志文件,日志文件可以是非常大的!

如果你想一行一行地阅读并且随时随地打印,那么除了使用支持诸如java.io.RandomAccessFile之类的阅读器之外别无select。