2011-12-29 47 views
12

Tôi muốn đọc tập tin trong hướng ngược lại từ đầu này đến đầu khởi động tập tin của tôi,Cách đọc tệp từ đầu đến cuối (theo thứ tự ngược) trong 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 

tôi sử dụng mã để đọc nó theo cách này,

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(); 

Tôi muốn đọc theo thứ tự ngược sử dụng bộ đọc đệm

+0

Mục đích đọc tệp từ đầu đến cuối là gì? – adatapost

+1

Cùng loại câu hỏi được trả lời http://stackoverflow.com/questions/6011345/read-a-file-line-by-line-in-reverse-order Check-out. – Zlatan

+0

bạn không thể chỉ đọc tệp và sử dụng phương thức reverse() trong StringBuilder? – asgs

Trả lời

5

Theo như tôi hiểu, bạn cố đọc từng dòng một. Giả sử đây là tập tin bạn cố gắng đọc:

line1
dòng2
Line3

Và bạn muốn viết nó vào dòng đầu ra của servlet như sau:

Line3
line2
line1

Mã sau có thể hữu ích trong trường hợp này:

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/>"); 
    } 
+5

Tôi khuyên bạn nên sử dụng bộ sưu tập Stack, vì nó là LIFO định hướng –

2
@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 
      } 
     } 
    } 
} 

Lưu ý rằng mã này đọc file lỗ vào bộ nhớ và sau đó bắt đầu in nó. Đây là cách duy nhất bạn có thể làm điều đó với một người đọc đệm hoặc người đọc khác anry mà không hỗ trợ tìm kiếm. Bạn phải ghi nhớ điều này, trong trường hợp bạn muốn đọc một tệp nhật ký, các tệp nhật ký có thể rất lớn!

Nếu bạn muốn đọc từng dòng và in khi đang di chuyển, bạn không còn cách nào khác thay vì sử dụng trình đọc hỗ trợ tìm kiếm như java.io.RandomAccessFile và bất kỳ điều gì khác nhưng tầm thường.

47

Tôi gặp vấn đề tương tự như được mô tả ở đây. Tôi muốn nhìn vào dòng trong tập tin theo thứ tự ngược lại, từ cuối trở lại để bắt đầu (Lệnh unix tac sẽ làm điều đó).

Tuy nhiên tệp đầu vào của tôi khá lớn nên đọc toàn bộ tệp vào bộ nhớ, như trong các ví dụ khác không thực sự là một tùy chọn khả thi đối với tôi.

Dưới đây là lớp tôi đã đưa ra, nó sử dụng RandomAccessFile, nhưng không cần bất kỳ bộ đệm nào vì nó chỉ giữ lại con trỏ tới chính tệp và làm việc với các phương thức chuẩn InputStream.

Nó hoạt động cho các trường hợp của tôi và các tệp trống và một vài thứ khác mà tôi đã thử. Bây giờ tôi không có các ký tự Unicode hoặc bất cứ điều gì ưa thích, nhưng miễn là các dòng được giới hạn bởi LF, và ngay cả khi họ có một LF + CR nó sẽ làm việc.

Cách sử dụng cơ bản là:

in = new BufferedReader (new InputStreamReader (new ReverseLineInputStream(file))); 

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

Đây là nguồn chính:

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(); 
     } 
    } 
} 
+2

Trong khi nó là đáng ngưỡng mộ rằng bạn đã mã hóa chính nó, bạn muốn * một số * hình thức đệm; nếu không, hiệu suất đọc từng byte một sẽ kém. http://stackoverflow.com/a/31961274/14731 có lẽ là một cách tốt hơn để đi. Tôi cũng mã hóa một giải pháp mà bây giờ tôi buộc phải ném ra :) – Gili

+0

@Gili đâu là giải pháp đó? –

+0

@steveenzoleko Đã qua rồi. Tôi đã xóa mã vì hiệu suất của nó quá kém. Bạn có thể sử dụng giải pháp trên như một giải pháp thay thế nếu hiệu suất không phải là vấn đề. – Gili

9

Các ReverseLineInputStream đăng ở trên là chính xác những gì tôi đang tìm kiếm. Các tệp tôi đang đọc lớn và không thể được lưu vào bộ đệm.

Có một vài lỗi:

  • tệp không được đóng
  • nếu dòng cuối cùng không được chấm dứt hợp 2 dòng cuối cùng được trả về vào đọc đầu tiên.

Đây là mã sửa:

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; 
     } 
    } 
} 
11

Apache Commons IO có lớp ReversedLinesFileReader cho điều này bây giờ (tốt, kể từ phiên bản 2.2).

Vì vậy, mã của bạn có thể là:

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(); 
6

đề xuất ReverseLineInputStream hoạt động thực sự chậm khi bạn cố gắng đọc hàng ngàn dòng. Tại PC của tôi Intel Core i7 trên ổ SSD khoảng 60k dòng trong 80 giây. Dưới đây là phiên bản được tối ưu hóa được lấy cảm hứng với đọc đệm (trái ngược với đọc một byte tại một thời gian trong ReverseLineInputStream). Tệp nhật ký dòng 60k được đọc trong 400 mili giây:

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; 
     } 
    } 
}} 
Các vấn đề liên quan