2010-05-20 4 views
11

통합 로그 파일 뷰어가있는 소프트웨어 제품에 대한 작업을하고 있습니다. 문제는 로그 파일을 볼 때 전체 파일을 메모리로 읽으므로 실제로는 큰 파일에 대해 속도가 느리고 불안정하다는 것입니다. 이 문제를 해결하는 새로운 로그 파일 뷰어를 작성하고 싶습니다.큰 로그 파일을위한 자바 텍스트 파일 뷰어 작성 방법

대용량 텍스트 파일의 시청자를 작성하는 가장 좋은 방법은 무엇입니까? 메모장 ++와 VIM 같은 편집자는 어떻게 이것을 성취합니까? 자바의 TableModel과 함께 버퍼 된 양방향 텍스트 스트림 리더를 사용하려고 생각했습니다. 내가 옳은 라인을 따라 생각하고 있으며 Java에서 사용할 수있는 스트림 구현입니까?

편집 : 파일을 한 번 실행하여 각 줄의 시작 위치를 인덱싱하여 어디서 탐색해야하는지 알 수있게 할 가치가 있습니까? 나는 아마 선의 양을 필요로 할 것이다. 그래서 아마 적어도 한 번 파일을 조사해야 할 것인가?

편집 2 : 아래의 답변에 구현을 추가했습니다. 의견을 보내 주시거나 편집하여보다 나은 모범 사례를 구현하거나 직접 제공하십시오.

답변

4

NotePad ++가 실제로 랜덤 액세스를 구현하는지는 확실하지 않지만, 특히 로그 파일 뷰어를 사용하는 것이 좋습니다. 이는 읽기 전용임을 의미합니다.

로그 뷰어가 읽기 전용이므로 메모리 매핑 된 파일 "스트림"random access 읽기 전용을 사용할 수 있습니다. 자바에서는 FileChannel입니다.

그런 다음 필요에 따라 파일을 뛰어 넘기 만하면 데이터 스크롤 창이 화면에 표시됩니다.

FileChannel의 장점 중 하나는 동시 스레드가 파일을 열 수 있고 읽기가 현재 파일 포인터에 영향을 미치지 않는다는 것입니다. 따라서 다른 스레드의 로그 파일에 추가하는 경우 영향을받지 않습니다.

또 다른 장점은 FileChannel의 size 메서드를 호출하여 언제든지 파일 크기를 가져올 수 있다는 것입니다.

일부 텍스트 편집기에서 허용하는 임의 액세스 파일 (예 : HxD 및 UltraEdit)에 메모리를 직접 매핑하는 문제는 모든 변경 내용이 파일에 직접 영향을 미친다는 것입니다. 따라서 변경 사항은 즉각적이며 (쓰기 캐싱 제외) 사용자가 일반적으로 원하지 않는 것입니다. 대신 사용자는 일반적으로 저장을 클릭 할 때까지 변경 사항을 원하지 않습니다. 그러나 이것은 단지 뷰어이기 때문에 같은 걱정거리가 없습니다.

+0

덕분에, 나는 또한 유용 –

2

일반적인 접근 방법은 탐색 가능 파일 판독기를 사용하고 줄 오프셋 색인을 기록하는 로그를 한 번 통과시킨 다음 요청 된대로 파일의 일부분에만 창을 표시하는 것입니다.

이렇게하면 빠른 호출에서 필요한 데이터가 줄어들고 내용의 99 %가 현재 표시되지 않는 위젯이로드되지 않습니다.

0

나는 (Marcus Adams와 msw의 조언에 따라) 테스트 구현을 게시합니다. 여기에는 사용자의 편의를 위해 추가 주석과 비판을하기 위해 나와 있습니다. 매우 빠릅니다.

저는 유니 코드 인코딩 안전에 신경을 쓰지 않았습니다. 내 다음 질문 일거야. 이것에 관한 어떤 힌트라도 환영 받는다.

class LogFileTableModel implements TableModel { 

    private final File f; 
    private final int lineCount; 
    private final String errMsg; 
    private final Long[] index; 
    private final ByteBuffer linebuf = ByteBuffer.allocate(1024); 
    private FileChannel chan; 

    public LogFileTableModel(String filename) { 
     f = new File(filename); 
     String m; 
     int l = 1; 
     Long[] idx = new Long[] {}; 
     try { 
      FileInputStream in = new FileInputStream(f); 
      chan = in.getChannel(); 
      m = null; 
      idx = buildLineIndex(); 
      l = idx.length; 
     } catch (IOException e) { 
      m = e.getMessage(); 
     } 
     errMsg = m; 
     lineCount = l; 
     index = idx; 
    } 

    private Long[] buildLineIndex() throws IOException { 
     List<Long> idx = new LinkedList<Long>(); 
     idx.add(0L); 

     ByteBuffer buf = ByteBuffer.allocate(8 * 1024); 
     long offset = 0; 
     while (chan.read(buf) != -1) { 
      int len = buf.position(); 
      buf.rewind();    
      int pos = 0; 
      byte[] bufA = buf.array(); 
      while (pos < len) { 
       byte c = bufA[pos++]; 
       if (c == '\n') 
        idx.add(offset + pos); 
      } 
      offset = chan.position(); 
     } 
     System.out.println("Done Building index"); 
     return idx.toArray(new Long[] {}); 
    } 

    @Override 
    public int getColumnCount() { 
     return 2; 
    } 

    @Override 
    public int getRowCount() { 
     return lineCount; 
    } 

    @Override 
    public String getColumnName(int columnIndex) { 
     switch (columnIndex) { 
     case 0: 
      return "#"; 
     case 1: 
      return "Name"; 
     } 
     return ""; 
    } 

    @Override 
    public Object getValueAt(int rowIndex, int columnIndex) { 
     switch (columnIndex) { 
      case 0:     
       return String.format("%3d", rowIndex); 
      case 1: 
       if (errMsg != null) 
        return errMsg; 
       try { 
        Long pos = index[rowIndex]; 
        chan.position(pos); 
        chan.read(linebuf); 
        linebuf.rewind(); 
        if (rowIndex == lineCount - 1) 
         return new String(linebuf.array()); 
        else  
         return new String(linebuf.array(), 0, (int)(long)(index[rowIndex+1]-pos)); 
       } catch (Exception e) { 
        return "Error: "+ e.getMessage(); 
       } 
     }    
     return "a"; 
    } 

    @Override 
    public Class<?> getColumnClass(int columnIndex) { 
     return String.class; 
    } 

    // ... other methods to make interface complete 


} 
+0

는 흠, 좋아, 내 구현처럼 보인다 증명할 수되는 FileChannel에 추가 RandomAccessFile의를 본 것은 때문에 UTF-8의 고유 한 자기 동기화 다움의 UTF-8이 안전합니다.바이너리 00100000 인 '\ n'을 확인하는 것은 UTF-8에서 고유합니다. 다중 바이트 시퀀스의 일부인 모든 바이트에는 최소한 비트 8 세트가 있습니다. –