Saturday, March 10, 2012

Reverse Line Reader

We would need a class to read the log files in reverse order to display the latest message.

   1: package com.whitelotus.util;
   2:  
   3: import java.io.ByteArrayOutputStream;
   4: import java.io.File;
   5: import java.io.IOException;
   6: import java.io.RandomAccessFile;
   7: import java.io.UnsupportedEncodingException;
   8: import java.nio.ByteBuffer;
   9: import java.nio.channels.FileChannel;
  10:  
  11:  
  12: public class ReverseLineReader {
  13:     private static final int BUFFER_SIZE = 8192;
  14:  
  15:     private final FileChannel channel;
  16:     private final String encoding;
  17:     private long filePos;
  18:     private ByteBuffer buf;
  19:     private int bufPos;
  20:     private byte lastLineBreak = '\n';
  21:     private ByteArrayOutputStream baos = new ByteArrayOutputStream();
  22:  
  23:     public ReverseLineReader(File file, String encoding) throws IOException {
  24:         RandomAccessFile raf = new RandomAccessFile(file, "r");
  25:         channel = raf.getChannel();
  26:         filePos = raf.length();
  27:         this.encoding = encoding;
  28:     }
  29:  
  30:     public String readLine() throws IOException {
  31:         while (true) {
  32:             if (bufPos < 0) {
  33:                 if (filePos == 0) {
  34:                     if (baos == null) {
  35:                         return null;
  36:                     }
  37:                     String line = bufToString();
  38:                     baos = null;
  39:                     return line;
  40:                 }
  41:  
  42:                 long start = Math.max(filePos - BUFFER_SIZE, 0);
  43:                 long end = filePos;
  44:                 long len = end - start;
  45:  
  46:                 buf = channel.map(FileChannel.MapMode.READ_ONLY, start,
  47:                         len);
  48:                 bufPos = (int) len;
  49:                 filePos = start;
  50:             }
  51:  
  52:             while (bufPos-- > 0) {
  53:                 byte c = buf.get(bufPos);
  54:                 if (c == '\r' || c == '\n') {
  55:                     if (c != lastLineBreak) {
  56:                         lastLineBreak = c;
  57:                         continue;
  58:                     }
  59:                     lastLineBreak = c;
  60:                     return bufToString();
  61:                 }
  62:                 baos.write(c);
  63:             }
  64:         }
  65:     }
  66:  
  67:     private String bufToString() throws UnsupportedEncodingException {
  68:         if (baos.size() == 0) {
  69:             return "";
  70:         }
  71:  
  72:         byte[] bytes = baos.toByteArray();
  73:         for (int i = 0; i < bytes.length / 2; i++) {
  74:             byte t = bytes[i];
  75:             bytes[i] = bytes[bytes.length - i - 1];
  76:             bytes[bytes.length - i - 1] = t;
  77:         }
  78:  
  79:         baos.reset();
  80:  
  81:         return new String(bytes, encoding);
  82:     }
  83: }

Using ReverseLineReader



   1: public static void main(String[] args) throws IOException {
   2:       File file = new File("tps.log");
   3:       RandomAccessFile raf = new RandomAccessFile(file, "rw");
   4:       for(int i=0;i<=10;i++) {
   5:           raf.writeBytes("HELLO " + i + "\n");
   6:       }
   7:       raf.close();
   8:       ReverseLineReader reader = new ReverseLineReader(file, "UTF-8");
   9:  
  10:       String line;
  11:       while ((line = reader.readLine()) != null) {
  12:           System.out.println(line);
  13:       }
  14:   }
  15:  
  16:  

1 comment:

  1. if the linefeed is '\r\n' , it won't work ;please fix this bug.

    ReplyDelete