Мне назвали файл file.txt
который является обновлением путем добавления строк к нему.
Я читаю его этим кодом:
$fp = fopen("file.txt", "r");
$data = "";
while(!feof($fp))
{
$data .= fgets($fp, 4096);
}
echo $data;
и появляется огромное количество строк. Я просто хочу повторить последние 5 строк файла
Как я могу сделать это?
file.txt
похож на это:
11111111111111
22222222222
33333333333333
44444444444
55555555555555
66666666666
Непроверенный код, но должен работать:
$file = file("filename.txt");
for ($i = max(0, count($file)-6); $i < count($file); $i++) {
echo $file[$i] . "\n";
}
Вызов max
обработает файл размером менее 6 строк.
Это частый вопрос интервью. Вот что я написал в прошлом году, когда мне задали этот вопрос. Помните, что код, который вы получаете на Stack Overflow, лицензирован Creative Commons Share-Alike с указанием авторства .
<?php
/**
* Demonstrate an efficient way to search the last 100 lines of a file
* containing roughly ten million lines for a sample string. This should
* function without having to process each line of the file (and without making
* use of the “tail” command or any external system commands).
* Attribution: https://stackoverflow.com/a/2961731/3389585
*/
$filename = '/opt/local/apache2/logs/karwin-access_log';
$searchString = 'index.php';
$numLines = 100;
$maxLineLength = 200;
$fp = fopen($filename, 'r');
$data = fseek($fp, -($numLines * $maxLineLength), SEEK_END);
$lines = array();
while (!feof($fp)) {
$lines[] = fgets($fp);
}
$c = count($lines);
$i = $c >= $numLines? $c-$numLines: 0;
for (; $i<$c; ++$i) {
if ($pos = strpos($lines[$i], $searchString)) {
echo $lines[$i];
}
}
Это решение делает предположение о максимальной длине линии. Интервьюер спросил меня, как бы я решил проблему, если бы не смог сделать это предположение, и мне пришлось бы использовать строки, которые потенциально были длиннее любой максимальной длины, которую я выбрал.
Я сказал ему, что любой программный проект должен делать определенные предположения, но я могу проверить, было ли $ c
меньше желаемого количества строк, а если нет, fseek ( )
назад постепенно (удваивая каждый раз), пока мы не получим достаточно строк.
Функция PHP file () считывает весь файл в массив. Это решение требует наименьшего количества ввода:
$data = array_slice(file('file.txt'), -5);
foreach ($data as $line) {
echo $line;
}
Если ваши строки разделены CR или LF, попробуйте взорвать вашу переменную $ data:
$lines = explode("\n", $data);
$ lines должны в конечном итоге стать массивом, и вы можете вычислить количество записей с помощью sizeof () и просто получите последние 5.
Если вы работаете в системе Linux, вы можете сделать это:
$lines = `tail -5 /path/to/file.txt`;
В противном случае вам придется подсчитывать строки и брать последние 5, что-то вроде:
$all_lines = file('file.txt');
$last_5 = array_slice($all_lines , -5);
Для большого файла считывание всех строк в массив с помощью file () немного расточительно. Вот как вы можете прочитать файл и сохранить буфер из последних 5 строк:
$lines=array();
$fp = fopen("file.txt", "r");
while(!feof($fp))
{
$line = fgets($fp, 4096);
array_push($lines, $line);
if (count($lines)>5)
array_shift($lines);
}
fclose($fp);
Вы можете немного оптимизировать это с помощью некоторых эвристик о вероятной длине строки, перейдя к позиции, скажем, примерно в 10 строках от конца, и возвращаясь дальше назад, если это не дает 5 строк. Вот простая реализация, демонстрирующая следующее:
//how many lines?
$linecount=5;
//what's a typical line length?
$length=40;
//which file?
$file="test.txt";
//we double the offset factor on each iteration
//if our first guess at the file offset doesn't
//yield $linecount lines
$offset_factor=1;
$bytes=filesize($file);
$fp = fopen($file, "r") or die("Can't open $file");
$complete=false;
while (!$complete)
{
//seek to a position close to end of file
$offset = $linecount * $length * $offset_factor;
fseek($fp, -$offset, SEEK_END);
//we might seek mid-line, so read partial line
//if our offset means we're reading the whole file,
//we don't skip...
if ($offset<$bytes)
fgets($fp);
//read all following lines, store last x
$lines=array();
while(!feof($fp))
{
$line = fgets($fp);
array_push($lines, $line);
if (count($lines)>$linecount)
{
array_shift($lines);
$complete=true;
}
}
//if we read the whole file, we're done, even if we
//don't have enough lines
if ($offset>=$bytes)
$complete=true;
else
$offset_factor*=2; //otherwise let's seek even further back
}
fclose($fp);
var_dump($lines);
function ReadFromEndByLine($filename,$lines)
{
/* freely customisable number of lines read per time*/
$bufferlength = 5000;
$handle = @fopen($filename, "r");
if (!$handle) {
echo "Error: can't find or open $filename<br/>\n";
return -1;
}
/*get the file size with a trick*/
fseek($handle, 0, SEEK_END);
$filesize = ftell($handle);
/*don't want to get past the start-of-file*/
$position= - min($bufferlength,$filesize);
while ($lines > 0) {
if ($err=fseek($handle,$position,SEEK_END)) { /* should not happen but it's better if we check it*/
echo "Error $err: something went wrong<br/>\n";
fclose($handle);
return $lines;
}
/* big read*/
$buffer = fread($handle,$bufferlength);
/* small split*/
$tmp = explode("\n",$buffer);
/*previous read could have stored a partial line in $aliq*/
if ($aliq != "") {
/*concatenate current last line with the piece left from the previous read*/
$tmp[count($tmp)-1].=$aliq;
}
/*drop first line because it may not be complete*/
$aliq = array_shift($tmp);
$read = count($tmp);
if ( $read >= $lines ) { /*have read too much!*/
$tmp2 = array_slice($tmp,$read-$n);
/* merge it with the array which will be returned by the function*/
$lines = array_merge($tmp2,$lines);
/* break the cycle*/
$lines = 0;
} elseif (-$position >= $filesize) { /* haven't read enough but arrived at the start of file*/
//get back $aliq which contains the very first line of the file
$lines = array_merge($aliq,$tmp,$lines);
//force it to stop reading
$lines = 0;
} else { /*continue reading...*/
//add the freshly grabbed lines on top of the others
$lines = array_merge($tmp,$lines);
$lines -= $read;
//next time we want to read another block
$position -= $bufferlength;
//don't want to get past the start of file
$position = max($position, -$filesize);
}
}
fclose($handle);
return $lines;
}
Это будет быстро для больших файлов, но много кода для простой задачи, если есть большие файлы, используйте это
ReadFromEndByLine('myFile.txt',6);