如何在PHP中只读取文本文件的最后一行?

我有一个名为“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 

未经testing的代码,但应该工作:

 $file = file("filename.txt"); for ($i = max(0, count($file)-6); $i < count($file); $i++) { echo $file[$i] . "\n"; } 

调用max将处理文件less于6行。

对于大文件,使用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);

这是一个常见的面试问题。 这是我去年写的这个问题。 请记住,您在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). */ $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]; } } 

该解决scheme确实对最大线路长度做出了假设。 面试官问我,如果我不能做出这样的假设,我将如何解决这个问题,并且不得不适应比我select的任何最大长度更长的线。

我告诉他,任何软件项目都必须做出某些假设,但是我可以testing$c是否小于所需的行数,如果不是,则fseek()每次增加一倍(每次fseek() )直到我们做获得足够的线路。

如果你在Linux系统上,你可以这样做:

 $lines = `tail -5 /path/to/file.txt`; 

否则,你将不得不数线,并采取最后5,如下所示:

 $all_lines = file('file.txt'); $last_5 = array_slice($all_lines , -5); 

这不使用file()所以对于大文件来说效率会更高。

 <?php function read_backward_line($filename, $lines, $revers = false) { $offset = -1; $c = ''; $read = ''; $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")); } //if $revers=false function return-> //line 1000: i am line of 1000 //line 1001: and i am line of 1001 //line 1002: and i am last line //but if $revers=true function return-> //line 1002: and i am last line //line 1001: and i am line of 1001 //line 1000: i am line of 1000 ?> 

这里的大部分选项假设将文件读入内存,然后使用行。 这不是一个好主意,如果文件太大

我认为最好的方法是使用一些操作系统实用程序,如unix中的“tail”。

 exec('tail -3 /logs/reports/2017/02-15/173606-arachni-2415.log', $output); echo $output; // 2017-02-15 18:03:25 [*] Path Traversal: Analyzing response ... // 2017-02-15 18:03:27 [*] Path Traversal: Analyzing response ... // 2017-02-15 18:03:27 [*] Path Traversal: Analyzing response ... 

PHP的file()函数将整个文件读入一个数组。 这个解决scheme需要最less量的input:

 $data = array_slice(file('file.txt'), -5); foreach ($data as $line) { echo $line; } 

使用file()打开大文件可以生成一个大数组,保留大量的内存。

您可以使用SplFileObject减less内存成本,因为它遍历每一行。

使用seekableiteratorseek方法获取最后一行。 然后你应该减去当前键值5。

要获得最后一行,请使用PHP_INT_MAX 。 (是的,这是一个解决方法。)

 $file = new SplFileObject('large_file.txt', 'r'); $file->seek(PHP_INT_MAX); $last_line = $file->key(); $lines = new LimitIterator($file, $last_line - 5, $last_line); print_r(iterator_to_array($lines)); 

这个function适用于4GB以下的真正大文件。 速度来自读取一大块数据,而不是一次一个字节,并计算行数。

 // Will seek backwards $n lines from the current position function seekLineBackFast($fh, $n = 1){ $pos = ftell($fh); if ($pos == 0) return false; $posAtStart = $pos; $readSize = 2048*2; $pos = ftell($fh); if(!$pos){ fseek($fh, 0, SEEK_SET); return false; } // we want to seek 1 line before the line we want. // so that we can start at the very beginning of the line while ($n >= 0) { if($pos == 0) break; $pos -= $readSize; if($pos <= 0){ $pos = 0; } // fseek returns 0 on success and -1 on error if(fseek($fh, $pos, SEEK_SET)==-1){ fseek($fh, 0, SEEK_SET); break; } $data = fread($fh, $readSize); $count = substr_count($data, "\n"); $n -= $count; if($n < 0) break; } fseek($fh, $pos, SEEK_SET); // we may have seeked too far back // so we read one line at a time forward while($n < 0){ fgets($fh); $n++; } // just in case? $pos = ftell($fh); if(!$pos) fseek($fh, 0, SEEK_SET); // check that we have indeed gone back if ($pos >= $posAtStart) return false; return $pos; } 

运行上面的函数之后,你可以在循环中做fgets()来从$ fh中每次读取每一行。

你可以使用我的小帮手库(2个function)

https://github.com/jasir/file-helpers

然后只使用:

 //read last 5 lines $lines = \jasir\FileHelpers\FileHelpers::readLastLines($pathToFile, 5); 

我testing了这一个。 它适用于我。

 function getlast($filename,$linenum_to_read,$linelength){ // this function takes 3 arguments; if (!$linelength){ $linelength = 600;} $f = fopen($filename, 'r'); $linenum = filesize($filename)/$linelength; for ($i=1; $i<=($linenum-$linenum_to_read);$i++) { $data = fread($f,$linelength); } echo "<pre>"; for ($j=1; $j<=$linenum_to_read+1;$j++) { echo fread($f,$linelength); } echo "</pre><hr />The filesize is:".filesize("$filename"); } getlast("file.txt",6,230); ?> 

最less量的公羊,产量很好。 我同意Paul Dixon的观点

 $lines=array(); $fp = fopen("userlog.txt", "r"); while(!feof($fp)) { $line = fgets($fp, 4096); array_push($lines, $line); if (count($lines)>25) array_shift($lines); } fclose($fp); while ($a <= 10) { $a++; echo "<br>".$lines[$a]; } 
 $dosya = "../dosya.txt"; $array = explode("\n", file_get_contents($dosya)); $reversed = array_reverse($array); for($x = 0; $x < 6; $x++) { echo $reversed[$x]; } 

如果你的行被一个CR或LF分开,你可以尝试爆炸你的$ datavariables:

 $lines = explode("\n", $data); 

$行最终应该是一个数组,你可以使用sizeof()来计算出logging的数量,然后得到最后5个logging。

这是从文本文件中读取的最后10行

 $data = array_slice(file('logs.txt'),10); foreach ($data as $line) { echo $line."<br/>"; }