2010-06-11 4 views
13

저는 Rails 앱의 로그 뷰어에서 작업 중이며 기본 위에서 아래로가 아니라 아래에서 위로 로그 파일을 200 줄 정도 읽어야한다는 것을 알고 있습니다.Ruby에서 파일을 아래에서 위로 읽는 방법?

로그 파일이 상당히 커질 수 있으므로 이미 IO.readlines ("log_file.log") [- 200 ..- 1] 방법을 시도하고 배제했습니다.

플러그인이나 보석없이 루비에서 파일을 거꾸로 읽는 다른 방법이 있습니까?

+0

중복 : 그래서 내가 molf 코드 강화 [? 루비에서 파일의 마지막 n 줄을 읽기] (http://stackoverflow.com/questions/754494) – hippietrail

답변

17

엄청난 파일에서도 작동하는 유일한 방법은 끝까지 한 번에 n 바이트를 읽는 것입니다. 이것은 본질적으로 Unix tail의 작동 방식입니다.

Array로 지난 n 라인 반환 IO#tail(n)의 예 구현 :

class IO 
    TAIL_BUF_LENGTH = 1 << 16 

    def tail(n) 
    return [] if n < 1 

    seek -TAIL_BUF_LENGTH, SEEK_END 

    buf = "" 
    while buf.count("\n") <= n 
     buf = read(TAIL_BUF_LENGTH) + buf 
     seek 2 * -TAIL_BUF_LENGTH, SEEK_CUR 
    end 

    buf.split("\n")[-n..-1] 
    end 
end 

구현은 조금 순진,하지만 빠른 벤치 마크는 말도 안되는 차이가이 간단한 구현은 이미 할 수있는 것을 보여줍니다 (

      user  system  total  real 
f.readlines[-200..-1] 7.150000 1.150000 8.300000 ( 8.297671) 
f.tail(200)    0.000000 0.000000 0.000000 ( 0.000367) 

벤치 마크 코드 :

,691 yes > yes.txt으로 생성 된 ~ 25메가바이트 파일) 테스트
require "benchmark" 

FILE = "yes.txt" 

Benchmark.bmbm do |b| 
    b.report "f.readlines[-200..-1]" do 
    File.open(FILE) do |f| 
     f.readlines[-200..-1] 
    end 
    end 

    b.report "f.tail(200)" do 
    File.open(FILE) do |f| 
     f.tail(200) 
    end 
    end 
end 

물론 other implementations이 이미 있습니다. 나는 아무 것도 시도하지 않았으므로 나는 최선의 것을 말할 수 없다.

+0

말인지 생각'TAIL_BUF_LENGTH = 2 ** 16' 또는'1 << 16'이며, 둘 다 '65536' (64Ki)로 평가됩니다. '2^16'은 binary exclusive-or이고'18'으로 평가됩니다. –

+0

위대한 작품! 벤치 마크상의 차이점은 readlines와 비교하면 미친 것입니다. 결과 배열의 각 줄에 해당 줄 번호를 출력 할 수 있습니까? 감사합니다. – ericalli

+0

@ two2twelve : 아니에요. 이 전체 연습의 * 전체 목적 *은 "아래에서 위로"라는 파일을 읽는 것입니다. (당신의 말이 아닌, 나의 것이지요.) 당신이 * bottom *에서 시작했다면, 당신은 당신이 어떤 라인 (파일의 상단 *에서부터 계산됩니다)을 알 수 있습니까? 아니면 밑바닥부터 세는 것을 의미 했습니까? 이 경우 버퍼에서 인덱스 i의 라인은 아래에서 n-i 번째 라인입니다. –

0

나는 몹시 멋진 답변에 대해 언급하기에는 너무 새롭기 때문에 별도의 답변으로 게시해야합니다. 로그 파일이 기록되는 동안이 기능을 사용하여 로그의 마지막 부분에 완료되었음을 알리는 문자열이 포함되어 있으며 파싱을 시작할 수 있습니다.

따라서 작은 파일을 처리하는 것이 중요합니다 (로그가 작을 때 핑할 수도 있음). 의

class IO 
    def tail(n) 
     return [] if n < 1 
     if File.size(self) < (1 << 16) 
      tail_buf_length = File.size(self) 
      return self.readlines.reverse[0..n-1] 
     else 
      tail_buf_length = 1 << 16 
     end 
     self.seek(-tail_buf_length,IO::SEEK_END) 
     out = "" 
     count = 0 
     while count <= n 
      buf  = self.read(tail_buf_length) 
      count += buf.count("\n") 
      out  += buf 
      # 2 * since the pointer is a the end , of the previous iteration 
      self.seek(2 * -tail_buf_length,IO::SEEK_CUR) 
     end 
     return out.split("\n")[-n..-1] 
    end 
end