我有一个200kb的文件,在多个页面中使用,但每个页面只需要文件中的1-2行,如果我知道行号,如何只读取我需要的这些行呢?
例如,如果我只需要第10行,我不想将所有行加载到内存中,只需加载第10行。
对不起我的英语不好!
echo memory_get_usage(), PHP_EOL; // 333200
$file = new SplFileObject('bible.txt'); // 996kb
$file->seek(5000); // jump to line 5000 (zero-based)
echo $file->current(), PHP_EOL; // output current line
echo memory_get_usage(), PHP_EOL; // 342984 vs 3319864 when using file()
current()
或者只是echo $file
。虽然我发现使用方法更清晰。你也可以使用fgets()
,但那会获取下一行。memory_get_usage
的调用,只是为了证明这种方法几乎不占用内存。文件内容是否会改变?如果是静态的或相对静态的,您可以建立一个偏移量列表,以便在需要读取数据时使用。例如,如果文件每年更改一次,但您每天读取它数百次,则可以预先计算出所需行的偏移量,并直接跳转到它们,如下所示:
$offsets = array();
while ($line = fread($filehandle)) { .... find line 10 .... }
$offsets[10] = ftell($filehandle); // store line 10's location
.... find next line
$offsets[20] = ftell($filehandle);
等等。之后,您可以轻松地跳转到该行的位置,方法如下:
$fh = fopen('file.txt', 'rb');
fseek($fh, $offsets[20]); // jump to line 20
但这可能完全是过度的。尝试对操作进行基准测试-比较执行旧式的“读取20行”的时间与预计算/跳转所需的时间。
<?php
$lines = array(1, 2, 10);
$handle = @fopen("/tmp/inputfile.txt", "r");
if ($handle) {
$i = 0;
while (!feof($handle)) {
$line = stream_get_line($handle, 1000000, "\n");
if (in_array($i, $lines)) {
echo $line;
$line = ''; // Don't forget to clean the buffer!
}
if ($i > end($lines)) {
break;
}
$i++;
}
fclose($handle);
}
?>
只需循环遍历它们而无需存储,例如:
$i = 1;
$file = fopen('file.txt', 'r');
while (!feof($file)) {
$line = fgets($file); // this gets whole line from the file;
if ($i == 10) {
break; // break on tenth line
}
$i ++;
}
为什么你只尝试加载前十行?难道你不知道加载所有这些行实际上是一个问题吗?
如果你没有测量过,那么你不知道它是一个问题。不要浪费时间优化非问题。除非你确切地知道加载该文件确实是瓶颈,否则你在不加载整个200K文件时所做的任何性能更改都几乎无法察觉。
seek
是基于行而不是基于字节的。 - Yacobyfgets
更好。 - davidtbernalSplFileInfo :: fseek()
和SplFileInfo :: seek()
。后者是基于行的,而另一个是基于字节的。seek()
是SeekableIterator
接口的方法。 - Gordonseek
到的行号并不是第5,000行。因为$line_pos
参数是从零开始计数的,所以此示例将会寻找在文本编辑器中看到的第5,001行。 - salathe