2011-12-29 5 views
12

voglio leggere file in direzione opposta da un capo all'altro del iniziare il mio file,Come leggere il file dall'inizio alla fine (nell'ordine inverso) in 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 

ho utilizzare il codice per leggerlo in questo modo,

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

io preferirei leggere in ordine inverso utilizzando il lettore di buffer

+0

Qual è lo scopo di leggere il file dall'inizio alla fine? – adatapost

+1

tipo di domanda si risponde Same http://stackoverflow.com/questions/6011345/read-a-file-line-by-line-in-reverse-order Partenza. – Zlatan

+0

non puoi semplicemente leggere il file e utilizzare il metodo reverse() in StringBuilder? – asgs

risposta

5

Per quanto ho capito, si tenta di leggere all'indietro, riga per riga. Supponiamo che questo è il file che si tenta di leggere:

riga1
linea2
line3

e si desidera scrivere nel flusso di output del servlet come segue:

line3
riga2
riga1

Il seguente codice potrebbe essere utile in questo caso:

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

mi consiglia di utilizzare la raccolta Catasta, dal momento che è orientata LIFO –

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

Si noti che questo codice legge il file buco nella memoria e quindi inizia a stamparlo. Questo è l'unico modo in cui puoi farlo con un lettore bufferizzato o un altro lettore che non supporta la ricerca. Devi tenerlo a mente, nel tuo caso vuoi leggere un file di log, i file di log possono essere molto grandi!

Se si desidera leggere riga per riga e stampare al volo, non si ha altra alternativa all'utilizzo di un lettore che supporta la ricerca come java.io.RandomAccessFile e questo tutt'altro che banale.

47

Ho avuto lo stesso problema descritto qui. Voglio guardare le righe nel file in ordine inverso, dalla fine indietro all'inizio (Il comando unix tac lo farà).

Tuttavia, i miei file di input sono abbastanza grandi, quindi la lettura dell'intero file è in memoria, in quanto negli altri esempi non era davvero un'opzione praticabile per me.

Di seguito è riportata la classe che utilizza, utilizza RandomAccessFile, ma non richiede alcun buffer, poiché mantiene solo i puntatori al file stesso e funziona con i metodi standard InputStream.

Funziona per i miei casi e file vuoti e alcune altre cose che ho provato. Ora non ho caratteri Unicode o cose fantasiose, ma finché le linee sono delimitate da LF, e anche se hanno un LF + CR dovrebbe funzionare.

Uso di base è:

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

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

Ecco la principale fonte:

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

Mentre è ammirevole che tu abbia codificato questo te stesso, vuoi * qualche * forma di buffering; in caso contrario, le prestazioni di lettura dei byte una alla volta saranno scarse. http://stackoverflow.com/a/31961274/14731 è probabilmente un modo migliore per andare. Ho anche codificato a mano una soluzione che ora sono costretta a buttare fuori :) – Gili

+0

@ Gili dove è quella soluzione? –

+0

@steveenzoleko Andato. Ho cancellato il codice perché le sue prestazioni erano troppo scarse. È possibile utilizzare la soluzione di cui sopra come alternativa se le prestazioni non sono un problema. – Gili

9

Il ReverseLineInputStream postato sopra è esattamente quello che stavo cercando. I file che sto leggendo sono grandi e non possono essere memorizzati nel buffer.

ci sono un paio di bug:

  • File non è chiuso
  • se l'ultima riga non è terminato le ultime 2 righe vengono restituiti al primo lettura.

Ecco il codice corretto:

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 ha la classe ReversedLinesFileReader per questo ora (bene, a partire dalla versione 2.2).

Così il vostro codice potrebbe essere:

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

La proposta ReverseLineInputStream funziona davvero lento quando si tenta di leggere migliaia di righe. Sul mio PC Intel Core i7 su unità SSD c'erano circa 60.000 linee in 80 secondi. Ecco la versione ottimizzata ispirata con bufferizzata lettura (opposta alla lettura di un byte alla volta in ReverseLineInputStream). Il file di log delle linee 60k viene letto in 400 millisecondi:

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; 
     } 
    } 
}} 
Problemi correlati