Я долго искал ответ и не нашел ничего, что работает правильно.
У меня есть файлы журналов, некоторые из которых достигают 100MB
, около 140,000
строк текста. С PHP
я пытаюсь получить последние 500
строк файла.
Как мне получить 500
строк? С большинством функций файл считывается в память, и это не правдоподобный случай для этого. Я предпочел бы избегать выполнения системных команд.
Если вы находитесь на машине nix, вы должны иметь возможность использовать экранирование оболочки и хвост инструмента. Это было какое-то время, но что-то вроде этого:
$lastLines = `tail -n 500`;
обратите внимание на использование меток, которые выполняют строку в BASH или аналогичные, и возвращает результаты.
Я написал эту функцию, которая кажется мне очень приятной. Он возвращает массив строк точно так же, как файл . Если вы хотите, чтобы он возвращал строку, такую как file_get_contents , просто измените оператор return implode('', array_reverse($lines));
для return implode('', array_reverse($lines));
:
function file_get_tail($filename, $num_lines = 10){ $file = fopen($filename, "r"); fseek($file, -1, SEEK_END); for ($line = 0, $lines = array(); $line < $num_lines && false !== ($char = fgetc($file));) { if($char === "\n"){ if(isset($lines[$line])){ $lines[$line][] = $char; $lines[$line] = implode('', array_reverse($lines[$line])); $line++; } }else $lines[$line][] = $char; fseek($file, -2, SEEK_CUR); } fclose($file); if($line < $num_lines) $lines[$line] = implode('', array_reverse($lines[$line])); return array_reverse($lines); }
Пример:
file_get_tail('filename.txt', 500);
Если вы хотите сделать это в PHP:
<?php /** Read last N lines from file. @param $filename string path to file. must support seeking @param $n int number of lines to get. @return array up to $n lines of text */ function tail($filename, $n) { $buffer_size = 1024; $fp = fopen($filename, 'r'); if (!$fp) return array(); fseek($fp, 0, SEEK_END); $pos = ftell($fp); $input = ''; $line_count = 0; while ($line_count < $n + 1) { // read the previous block of input $read_size = $pos >= $buffer_size ? $buffer_size : $pos; fseek($fp, $pos - $read_size, SEEK_SET); // prepend the current block, and count the new lines $input = fread($fp, $read_size).$input; $line_count = substr_count(ltrim($input), "\n"); // if $pos is == 0 we are at start of file $pos -= $read_size; if (!$pos) break; } fclose($fp); // return the last 50 lines found return array_slice(explode("\n", rtrim($input)), -$n); } var_dump(tail('/var/log/syslog', 50));
Это в значительной степени непроверено, но должно быть достаточно для того, чтобы вы получили полностью работоспособное решение.
Размер буфера равен 1024, но может быть изменен на больший или больший. (Вы даже можете динамически установить его на основе $ n * оценки длины строки.) Это должно быть лучше, чем поиск символа по символу, хотя это означает, что нам нужно сделать substr_count()
чтобы искать новые строки.