2012-05-23 33 views
5

Tương tự như: How to read only 5 last line of the text file in PHP?PHP: Đọc từ điểm nhất định trong tập tin

Tôi có một file log lớn và tôi muốn để có thể hiển thị 100 dòng từ vị trí X trong file. Tôi cần sử dụng fseek thay vì file() vì tệp nhật ký quá lớn.

Tôi có chức năng tương tự nhưng nó sẽ chỉ đọc từ cuối tệp. Làm thế nào nó có thể được sửa đổi để một vị trí bắt đầu có thể được xác định là tốt? Tôi cũng sẽ cần phải bắt đầu ở phần cuối của tập tin.

function read_line($filename, $lines, $revers = false) 
{ 
    $offset = -1; 
    $i = 0; 
    $fp = @fopen($filename, "r"); 
    while($lines && fseek($fp, $offset, SEEK_END) >= 0) { 
     $c = fgetc($fp); 
     if($c == "\n" || $c == "\r"){ 
      $lines--; 
      if($revers){ 
       $read[$i] = strrev($read[$i]); 
       $i++; 
      } 
     } 
     if($revers) $read[$i] .= $c; 
     else $read .= $c; 
     $offset--; 
    } 
    fclose ($fp); 
    if($revers){ 
     if($read[$i] == "\n" || $read[$i] == "\r") 
      array_pop($read); 
     else $read[$i] = strrev($read[$i]); 
     return implode('',$read); 
    } 
    return strrev(rtrim($read,"\n\r")); 
} 

Điều tôi đang cố gắng tạo trình xem nhật ký dựa trên web sẽ bắt đầu từ cuối tệp và hiển thị 100 dòng và khi nhấn nút "Tiếp theo", 100 dòng tiếp theo trước sẽ được hiển thị.

+0

Dưới đây là chưa nhiều cách để làm điều này theo thứ tự hiệu quả: http://unix.stackexchange.com/questions/94318/awk-or-sed-efficiency#94320 – dukevin

Trả lời

3

này sử dụng fseek đọc 100 dòng của một tập tin bắt đầu từ một quy định bù đắp. Nếu độ lệch lớn hơn số dòng trong nhật ký, 100 dòng đầu tiên sẽ được đọc.

Trong ứng dụng của mình, bạn có thể chuyển giá trị hiện tại qua chuỗi truy vấn cho prevtiếp theo và căn cứ vào khoản bù tiếp theo. Bạn cũng có thể lưu trữ và chuyển vị trí tệp hiện tại để có hiệu quả hơn.

<?php 

$GLOBALS["interval"] = 100; 

read_log(); 

function read_log() 
{ 
    $fp = fopen("log", "r"); 
    $offset = determine_offset(); 
    $interval = $GLOBALS["interval"]; 
    if (seek_to_offset($fp, $offset) != -1) 
    { 
     show_next_button($offset, $interval); 
    } 
    $lines = array(); 
    for ($ii = 0; $ii < $interval; $ii++) 
    { 
     $lines[] = trim(fgets($fp)); 
    } 
    echo "<pre>"; 
    print_r(array_reverse($lines)); 
} 

// Get the offset from the query string or default to the interval 
function determine_offset() 
{ 
    $interval = $GLOBALS["interval"]; 
    if (isset($_GET["offset"])) 
    { 
     return intval($_GET["offset"]) + $interval; 
    } 
    return $interval; 
} 

function show_next_button($offset, $interval) 
{ 
    $next_offset = $offset + $interval; 
    echo "<a href=\"?offset=" . $offset . "\">Next</a>"; 
} 

// Seek to the end of the file, then seek backward $offset lines 
function seek_to_offset($fp, $offset) 
{ 
    fseek($fp, 0, SEEK_END); 
    for ($ii = 0; $ii < $offset; $ii++) 
    { 
     if (seek_to_previous_line($fp) == -1) 
     { 
     rewind($fp); 
     return -1; 
     } 
    } 
} 

// Seek backward by char until line break 
function seek_to_previous_line($fp) 
{ 
    fseek($fp, -2, SEEK_CUR); 
    while (fgetc($fp) != "\n") 
    { 
     if (fseek($fp, -2, SEEK_CUR) == -1) 
     { 
     return -1; 
     } 
    } 
} 
+0

Nếu số lượng dòng trong tệp không được biết và tôi muốn bắt đầu xem từ cuối tệp, tôi sẽ sử dụng mã của bạn như thế nào? – dukevin

+0

nevermind, lệnh linux 'wc -l my_log.log' sẽ xuất số dòng – dukevin

+0

Tôi không chắc chắn bạn sẽ cần điều đó. Nếu bạn vượt qua một khoảng trống lớn hơn số dòng, điều này chỉ hiển thị 100 dòng ở đầu nhật ký. 'fseek' trả về -1 khi nó cố tìm kiếm trong quá khứ. Tôi sẽ thêm một bình luận nơi nó chạm vào đầu của tập tin. – Devourant

0

tôi sẽ làm điều đó như sau:

function readFileFunc($tempFile){ 
    if(@!file_exists($tempFile)){ 
     return FALSE; 
    }else{ 
     return file($tempFile); 
    } 
} 
$textArray = readFileFunc('./data/yourTextfile.txt'); 
$slicePos = count($textArray)-101; 
if($slicePos < 0){ 
    $slicePos = 0; 
} 
$last100 = array_slice($textArray, $slicePos); 
$last100 = implode('<br />', $last100); 
echo $last100; 
+0

Tôi không chắc chắn nếu PHP regex chức năng có thể xử lý văn bản lớn. – flowfree

+0

Chắc chắn nó CÓ THỂ !!!!! Và ít nhất nó nhanh hơn nhiều so với vòng lặp. Tôi có một dự án gần đây với một tập tin văn bản của 48.1MB với hơn 500'000 dòng và nó hoạt động nhanh hơn nhiều so với looping hoặc trong khi thông qua điều đó. nhưng tùy bạn! Học cụm từ thông dụng và bạn sẽ thấy lập trình khác nhau http://www.regular-expressions.info/reference.html – systrue

+0

Khi người dùng đã gặp sự cố về bộ nhớ, hãy lưu nội dung tệp vào một biến để phân tích, trong khi chắc chắn nhanh hơn, vẫn sẽ mất lên rất nhiều bộ nhớ trên một tập tin rất lớn. –

1

là "vị trí X" đo bằng dòng hoặc byte? Nếu dòng, bạn có thể dễ dàng sử dụng SplFileObject để tìm cách một dòng nhất định và sau đó đọc 100 dòng:

$file = new SplFileObject('log.txt'); 
$file->seek(199); // go to line 200 

for($i = 0; $i < 100 and $file->valid(); $i++, $file->next()) 
{ 
    echo $file->current(); 
} 

Nếu vị trí X được đo bằng byte, không phải là nó một vấn đề đơn giản của việc thay đổi ban đầu $offset = -1 của bạn đến một khác nhau giá trị?

+0

Điều này tốt đẹp nhưng tôi muốn đọc từ cuối tập tin đầu tiên. Nhưng độ dài của tập tin không được biết. – dukevin

+0

Lệnh linux 'wc -l my_log.log' sẽ in số thứ tự dòng – dukevin

3

Nếu bạn đang ở trên Unix, bạn có thể sử dụng các công cụ sed. Ví dụ: để có được dòng 10-20 từ một tập tin:

sed -n 10,20p errors.log 

Và bạn có thể làm điều này trong kịch bản của bạn:

<?php 
$page = 1; 
$limit = 100; 
$off = ($page * $limit) - ($limit - 1); 

exec("sed -n $off,".($limit+$off-1)."p errors.log", $out); 
print_r($out); 

Các dòng có sẵn trong $out mảng.

+0

Công việc này tuyệt vời, nhưng tôi muốn có thể đọc từ cuối tệp nhật ký và số dòng trong tệp là không biết – dukevin

+1

nevermind, lệnh linux 'wc -l my_log.log' sẽ xuất kết quả này. Chúc mừng! – dukevin

Các vấn đề liên quan