我可以从我的头读取PHP中的文件,例如,如果我想阅读最后10-20行吗?
而且,正如我读到的,如果文件的大小超过10mbs,我开始得到错误。
如何防止此错误?
为了阅读普通文件,我们使用代码:
if ($handle) {
while (($buffer = fgets($handle, 4096)) !== false) {
$i1++;
$content[$i1]=$buffer;
}
if (!feof($handle)) {
echo "Error: unexpected fgets() fail\n";
}
fclose($handle);
}
我的文件可能超过10mbs,但我只需阅读最后几行。我该怎么做?
由于
答案 0 :(得分:16)
您可以使用fopen和fseek从end向后导航文件。例如
$fp = @fopen($file, "r");
$pos = -2;
while (fgetc($fp) != "\n") {
fseek($fp, $pos, SEEK_END);
$pos = $pos - 1;
}
$lastline = fgets($fp);
答案 1 :(得分:6)
这取决于你如何解释“可以”。
如果您想知道是否可以直接执行此操作(使用PHP函数)而不阅读前面的所有行,那么答案是:否,您不能。
行结尾是对数据的解释,如果您实际读取了数据,则只能知道它们的位置。
如果它是一个非常大的文件,我不会这样做。 如果你从最后开始扫描文件会更好,并逐渐从最后读取块到文件。
<强>更新强>
这是一种仅限PHP 方式来读取文件的最后 n 行,而无需阅读所有文件:
function last_lines($path, $line_count, $block_size = 512){
$lines = array();
// we will always have a fragment of a non-complete line
// keep this in here till we have our next entire line.
$leftover = "";
$fh = fopen($path, 'r');
// go to the end of the file
fseek($fh, 0, SEEK_END);
do{
// need to know whether we can actually go back
// $block_size bytes
$can_read = $block_size;
if(ftell($fh) < $block_size){
$can_read = ftell($fh);
}
// go back as many bytes as we can
// read them to $data and then move the file pointer
// back to where we were.
fseek($fh, -$can_read, SEEK_CUR);
$data = fread($fh, $can_read);
$data .= $leftover;
fseek($fh, -$can_read, SEEK_CUR);
// split lines by \n. Then reverse them,
// now the last line is most likely not a complete
// line which is why we do not directly add it, but
// append it to the data read the next time.
$split_data = array_reverse(explode("\n", $data));
$new_lines = array_slice($split_data, 0, -1);
$lines = array_merge($lines, $new_lines);
$leftover = $split_data[count($split_data) - 1];
}
while(count($lines) < $line_count && ftell($fh) != 0);
if(ftell($fh) == 0){
$lines[] = $leftover;
}
fclose($fh);
// Usually, we will read too many lines, correct that here.
return array_slice($lines, 0, $line_count);
}
答案 2 :(得分:5)
它不是纯PHP,但常见的解决方案是使用tac命令,它是cat
的恢复,并反向加载文件。使用exec()或passthru()在服务器上运行它,然后读取结果。用法示例:
<?php
$myfile = 'myfile.txt';
$command = "tac $myfile > /tmp/myfilereversed.txt";
exec($command);
$currentRow = 0;
$numRows = 20; // stops after this number of rows
$handle = fopen("/tmp/myfilereversed.txt", "r");
while (!feof($handle) && $currentRow <= $numRows) {
$currentRow++;
$buffer = fgets($handle, 4096);
echo $buffer."<br>";
}
fclose($handle);
?>
答案 3 :(得分:3)
如果您的代码无效并报告错误,则应在帖子中包含错误!
您收到错误的原因是您尝试将文件的全部内容存储在PHP的内存空间中。
解决这个问题最有效的方法就是格林西莎建议并寻求文件的结尾然后再回过头来。但是,格林西娅回归的机制并不是非常有效。请考虑从流中获取最后几行的方法(即无法寻找的地方):
while (($buffer = fgets($handle, 4096)) !== false) {
$i1++;
$content[$i1]=$buffer;
unset($content[$i1-$lines_to_keep]);
}
因此,如果你知道你的最大行长度是4096,那么你会:
if (4096*lines_to_keep<filesize($input_file)) {
fseek($fp, -4096*$lines_to_keep, SEEK_END);
}
然后应用我之前描述的循环。
由于C有一些更有效的方法来处理字节流,因此最快的解决方案(在POSIX / Unix / Linux / BSD上)将是简单的:
$last_lines=system("last -" . $lines_to_keep . " filename");
答案 4 :(得分:3)
以下代码段为我工作。
$ file = popen(“tac $ filename”,“r”);
while($ line = fgets($ file)){
echo $line;
}
参考:http://laughingmeme.org/2008/02/28/reading-a-file-backwards-in-php/
答案 5 :(得分:3)
对于Linux,您可以
$linesToRead = 10;
exec("tail -n{$linesToRead} {$myFileName}" , $content);
您将在$ content变量中获得一系列行
纯PHP解决方案
$f = fopen($myFileName, 'r');
$maxLineLength = 1000; // Real maximum length of your records
$linesToRead = 10;
fseek($f, -$maxLineLength*$linesToRead, SEEK_END); // Moves cursor back from the end of file
$res = array();
while (($buffer = fgets($f, $maxLineLength)) !== false) {
$res[] = $buffer;
}
$content = array_slice($res, -$linesToRead);
答案 6 :(得分:2)
这是另一种解决方案。它在fgets()中没有行长度控制,你可以添加它。
/* Read file from end line by line */
$fp = fopen( dirname(__FILE__) . '\\some_file.txt', 'r');
$lines_read = 0;
$lines_to_read = 1000;
fseek($fp, 0, SEEK_END); //goto EOF
$eol_size = 2; // for windows is 2, rest is 1
$eol_char = "\r\n"; // mac=\r, unix=\n
while ($lines_read < $lines_to_read) {
if (ftell($fp)==0) break; //break on BOF (beginning...)
do {
fseek($fp, -1, SEEK_CUR); //seek 1 by 1 char from EOF
$eol = fgetc($fp) . fgetc($fp); //search for EOL (remove 1 fgetc if needed)
fseek($fp, -$eol_size, SEEK_CUR); //go back for EOL
} while ($eol != $eol_char && ftell($fp)>0 ); //check EOL and BOF
$position = ftell($fp); //save current position
if ($position != 0) fseek($fp, $eol_size, SEEK_CUR); //move for EOL
echo fgets($fp); //read LINE or do whatever is needed
fseek($fp, $position, SEEK_SET); //set current position
$lines_read++;
}
fclose($fp);
答案 7 :(得分:1)
在搜索相同的内容时,我可以跨越以下内容并认为它可能对其他人有用,所以在此分享:
/ *逐行读取文件* /
function tail_custom($filepath, $lines = 1, $adaptive = true) {
// Open file
$f = @fopen($filepath, "rb");
if ($f === false) return false;
// Sets buffer size, according to the number of lines to retrieve.
// This gives a performance boost when reading a few lines from the file.
if (!$adaptive) $buffer = 4096;
else $buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096));
// Jump to last character
fseek($f, -1, SEEK_END);
// Read it and adjust line number if necessary
// (Otherwise the result would be wrong if file doesn't end with a blank line)
if (fread($f, 1) != "\n") $lines -= 1;
// Start reading
$output = '';
$chunk = '';
// While we would like more
while (ftell($f) > 0 && $lines >= 0) {
// Figure out how far back we should jump
$seek = min(ftell($f), $buffer);
// Do the jump (backwards, relative to where we are)
fseek($f, -$seek, SEEK_CUR);
// Read a chunk and prepend it to our output
$output = ($chunk = fread($f, $seek)) . $output;
// Jump back to where we started reading
fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);
// Decrease our line counter
$lines -= substr_count($chunk, "\n");
}
// While we have too many lines
// (Because of buffer size we might have read too many)
while ($lines++ < 0) {
// Find first newline and remove all text before that
$output = substr($output, strpos($output, "\n") + 1);
}
// Close file and return
fclose($f);
return trim($output);
}
答案 8 :(得分:1)
如果你知道线条的长度,你可以避免很多黑魔法,只需抓住文件末尾的一大块。
我需要一个非常大的日志文件的最后15行,总共大约3000个字符。所以我只是抓住最后8000个字节是安全的,然后正常读取文件并从最后获取我需要的东西。
$fh = fopen($file, "r");
fseek($fh, -8192, SEEK_END);
$lines = array();
while($lines[] = fgets($fh)) {}
这可能比最高级别的答案更有效,后者逐个字符地读取文件,比较每个字符,并根据换行符进行拆分。
答案 9 :(得分:0)
正如爱因斯坦所说,每件事都应尽可能简单但不简单。此时,您需要一个数据结构,一个LIFO数据结构或简单地放置一个堆栈。