2010-07-13 69 views
12

Làm thế nào để đọc một tập tin ngược dòng bằng cách sử dụng fseek?Đọc một tập tin ngược dòng bằng cách sử dụng fseek

mã có thể hữu ích. phải là nền tảng chéo và php thuần túy.

nhiều cảm ơn trước

liên quan

Jera

+0

Có lý do nào tại sao nó phải được tìm kiếm thay vì các đề xuất bên dưới liên quan đến việc đọc toàn bộ tệp không? Bạn đang đọc loại dữ liệu nào? – salathe

+0

Nếu bạn muốn có thêm ** giải pháp thân thiện với bộ nhớ ** - đây là câu trả lời của tôi để trả lời câu hỏi tương tự: http://stackoverflow.com/questions/20561480/read-file-lines-backwards-fgets-with-php/ 26595154 # 26595154 –

Trả lời

14

Câu hỏi đặt ra là yêu cầu sử dụng fseek, vì vậy chỉ có thể giả định rằng hiệu suất là một vấn đề và tệp() không phải là giải pháp. Đây là một phương pháp đơn giản sử dụng fseek:

file.txt My

#file.txt 
Line 1 
Line 2 
Line 3 
Line 4 
Line 5 

Và mã:

<?php 

$fp = fopen('file.txt', 'r'); 

$pos = -2; // Skip final new line character (Set to -1 if not present) 

$lines = array(); 
$currentLine = ''; 

while (-1 !== fseek($fp, $pos, SEEK_END)) { 
    $char = fgetc($fp); 
    if (PHP_EOL == $char) { 
      $lines[] = $currentLine; 
      $currentLine = ''; 
    } else { 
      $currentLine = $char . $currentLine; 
    } 
    $pos--; 
} 

$lines[] = $currentLine; // Grab final line 

var_dump($lines); 

Output:

array(5) { 
    [0]=> 
    string(6) "Line 5" 
    [1]=> 
    string(6) "Line 4" 
    [2]=> 
    string(6) "Line 3" 
    [3]=> 
    string(6) "Line 2" 
    [4]=> 
    string(6) "Line 1" 
} 

Bạn không cần phải nối thêm với mảng $ lines như tôi, bạn có thể in kết quả ngay lập tức nếu đó là mục đích của tập lệnh của bạn. Ngoài ra nó rất dễ dàng để giới thiệu một truy cập nếu bạn muốn giới hạn số lượng các dòng.

$linesToShow = 3; 
$counter = 0; 
while ($counter <= $linesToShow && -1 !== fseek($fp, $pos, SEEK_END)) { 
    // Rest of code from example. After $lines[] = $currentLine; add: 
    $counter++; 
} 
+0

Sau khi chỉnh sửa của tôi bị từ chối ...: Bạn nên kiểm tra xem trình xử lý tệp có hợp lệ sau fopen() và sau đó được miễn phí với fclose() để giải phóng nó nhằm ngăn chặn rò rỉ bộ nhớ. – StanE

+0

PHP_EOL là nền tảng phụ thuộc và trong một số trường hợp sẽ không đủ. Thay thế điều kiện bằng ... ("\ n" == $ char) ... và bỏ qua các dòng trống sẽ làm cho nó đáng tin cậy hơn – ANTARA

6

Bạn không thể fseek từng dòng, bởi vì bạn không biết dòng này là bao lâu cho đến khi bạn đọc chúng.

Bạn nên đọc toàn bộ tệp vào danh sách các dòng hoặc nếu tệp quá lớn và bạn chỉ cần dòng cuối cùng, đọc khối có kích thước cố định từ cuối tệp và triển khai thêm một chút logic phức tạp phát hiện các dòng từ dữ liệu đó.

21

Nếu bạn định đọc toàn bộ tệp trong anyways, chỉ cần sử dụng file() để đọc tệp dưới dạng mảng (mỗi dòng là mỗi phần tử trong mảng) và sau đó sử dụng array_reverse() để lật mảng ngược và lặp lại . Hoặc chỉ cần làm ngược lại cho vòng lặp nơi bạn bắt đầu ở cuối và giảm trên mỗi vòng lặp.

$file = file("test.txt"); 
$file = array_reverse($file); 
foreach($file as $f){ 
    echo $f."<br />"; 
} 
+1

Hoặc để tránh đảo ngược mảng, bạn chỉ có thể lặp từ $ i = count ($ file) - 1; $ i> 0; $ i ++) và đọc $ file [$ i] –

+0

@EqSum yep, có thể làm điều đó. Phần giữa của vòng lặp nên là '$ i> = 0'. –

+0

Hey! Nếu bạn đang phân tích cú pháp một tệp thực sự lớn (hàng triệu dòng) thì điều này sẽ nhanh chóng sử dụng hết bộ nhớ bằng cách tải toàn bộ nội dung vào bộ nhớ. Đây không phải là một ý tưởng hay! –

8

Để hoàn toàn đảo ngược một tập tin:

 
$fl = fopen("\some_file.txt", "r"); 
for($x_pos = 0, $output = ''; fseek($fl, $x_pos, SEEK_END) !== -1; $x_pos--) { 
    $output .= fgetc($fl); 
    } 
fclose($fl); 
print_r($output); 

Tất nhiên, bạn muốn line-by-line đảo ngược ...

 

$fl = fopen("\some_file.txt", "r"); 
for($x_pos = 0, $ln = 0, $output = array(); fseek($fl, $x_pos, SEEK_END) !== -1; $x_pos--) { 
    $char = fgetc($fl); 
    if ($char === "\n") { 
     // analyse completed line $output[$ln] if need be 
     $ln++; 
     continue; 
     } 
    $output[$ln] = $char . ((array_key_exists($ln, $output)) ? $output[$ln] : ''); 
    } 
fclose($fl); 
print_r($output); 

Thật sự mặc dù, Jonathan Kuhn có câu trả lời tốt nhất IMHO ở trên. Các trường hợp duy nhất bạn không sử dụng câu trả lời mà tôi biết là nếu file hoặc các chức năng giống như bị vô hiệu hóa qua php.ini, nhưng quản trị viên đã quên về fseek hoặc khi mở một tệp lớn chỉ nhận được một vài dòng nội dung mới tiết kiệm bộ nhớ theo cách này.

Lưu ý: Xử lý lỗi không được bao gồm. Và, PHP_EOL không hợp tác, vì vậy tôi đã sử dụng "\ n" để biểu thị kết thúc của dòng thay thế. Vì vậy, ở trên có thể không hoạt động trong mọi trường hợp.

+0

Mã từ phần 2, phân tích từng dòng không hoạt động. –

2

Đọc toàn bộ tệp vào một mảng và đảo ngược là tốt trừ khi tệp lớn.

Bạn có thể thực hiện một đọc đệm của tập tin của bạn từ sau ra trước với một cái gì đó như thế này:

  • thiết lập một BUFFER_SIZE B - điều này nên dài hơn so với dòng dự kiến ​​dài nhất nếu không bạn sẽ cần một số logic cho tăng kích thước bộ đệm khi dòng quá dài
  • bộ bù đắp chiều dài = file - BUFFER_SIZE
  • trong khi bù đắp> = 0
    • đọc BUFFER_SIZE byte từ bù đắp
    • đọc một dòng - nó sẽ không đầy đủ khi chúng tôi đã nhảy vào giữa một dòng, vì vậy chúng tôi muốn đảm bảo bộ đệm tiếp theo mà chúng tôi đọc kết thúc bằng nó.Set bù đắp = bù đắp - BUFFER_SIZE + đường dài
    • loại bỏ dòng đó, đọc tất cả các dòng sau vào một mảng và ngược lại họ
    • quá trình mảng này để làm bất cứ điều gì bạn muốn làm
13
<?php 

class ReverseFile implements Iterator 
{ 
    const BUFFER_SIZE = 4096; 
    const SEPARATOR = "\n"; 

    public function __construct($filename) 
    { 
     $this->_fh = fopen($filename, 'r'); 
     $this->_filesize = filesize($filename); 
     $this->_pos = -1; 
     $this->_buffer = null; 
     $this->_key = -1; 
     $this->_value = null; 
    } 

    public function _read($size) 
    { 
     $this->_pos -= $size; 
     fseek($this->_fh, $this->_pos); 
     return fread($this->_fh, $size); 
    } 

    public function _readline() 
    { 
     $buffer =& $this->_buffer; 
     while (true) { 
      if ($this->_pos == 0) { 
       return array_pop($buffer); 
      } 
      if (count($buffer) > 1) { 
       return array_pop($buffer); 
      } 
      $buffer = explode(self::SEPARATOR, $this->_read(self::BUFFER_SIZE) . $buffer[0]); 
     } 
    } 

    public function next() 
    { 
     ++$this->_key; 
     $this->_value = $this->_readline(); 
    } 

    public function rewind() 
    { 
     if ($this->_filesize > 0) { 
      $this->_pos = $this->_filesize; 
      $this->_value = null; 
      $this->_key = -1; 
      $this->_buffer = explode(self::SEPARATOR, $this->_read($this->_filesize % self::BUFFER_SIZE ?: self::BUFFER_SIZE)); 
      $this->next(); 
     } 
    } 

    public function key() { return $this->_key; } 
    public function current() { return $this->_value; } 
    public function valid() { return ! is_null($this->_value); } 
} 

$f = new ReverseFile(__FILE__); 
foreach ($f as $line) echo $line, "\n"; 
0

Mã này đọc tệp ngược. Mã này bỏ qua sửa đổi về đọc, ví dụ apache access.log dòng mới trên procressing.

$f = fopen('FILE', 'r'); 

fseek($f, 0, SEEK_END); 

$pos = ftell($f); 
$pos--; 

while ($pos > 0) { 
    $chr = fgetc($f); 
    $pos --; 

    fseek($f, $pos); 

    if ($chr == PHP_EOL) { 
     YOUR_OWN_FUNCTION($rivi); 
     $rivi = NULL; 
     continue; 
    } 

    $rivi = $chr.$rivi; 
} 

fclose($f); 
Các vấn đề liên quan