Ещё раз о чтении файла на PHP...
...когда он достаточно велик. Неразумно пытаться прочитать весь файл в оперативку функцией вроде file_get_contents или file, например, файл из 2000000 строк уже не читается даже при установке в настройках php.ini значения memory_limit=128М (у меня на хостинге именно столько):
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 24 bytes) in Z:\home\localhost\www\read1.php on line 2
Тестовый скрипт, приведший к этой ошибке:
<?php
$a = file('data.txt');
$n = rand (0,count($a));
echo $a[$n];
?>
Файл данных data.txt был сформирован следующим скриптом:
<?php
$cnt = 2000000;
$f = fopen ('data.txt','w');
for ($i=1; $i<=$cnt; $i++) {
fwrite ($f,$i.($i<$cnt?"\n":""));
}
fclose ($f);
?>
То есть, в нём 2 миллиона строк с числами.
Теперь прочитаем файл построчно с помощью функции fgets, заодно выбирая из него случайную строку:
<?php
function rand_str ($f) {
$res='';
$i=0;
while (!feof($f)) {
$str = trim(fgets($f,128));
if (rand(0,$i++)==0) { $res=$str; }
}
return $res;
}
$f = fopen ('data.txt',"r");
$t1 = time ();
echo rand_str($f);
$t2 = time ();
echo '<br>'.($t2-$t1).'c.';
echo '<br>'.memory_get_usage(true);
?>
Обратите внимание, что случайная строка выбирается за один проход по файлу.
Этот скрипт выполняется успешно - ведь мы нигде не требуем иметь в оперативной памяти все данные сразу. Две последние строки кода контролируют время в секундах на выполнение основной части скрипта и использование оперативной памяти:
12c. 786432
На самом деле, удобней было бы реализовать класс, содержащий функционал для получения случайной строки из файла, имя которого передаётся конструктору класса параметром. В простейшем случае реализация класса и пример его вызова могут быть такими:
<?php
class reader {
private $f;
private function rand_str () {
$res='';
$i=0;
while (!feof($this->f)) {
$str = trim(fgets($this->f,128));
if (rand(0,$i++)==0) { $res=$str; }
}
return $res;
}
function __construct ($filename) {
$this->f = fopen ($filename,"r");
}
function __destruct () {
fclose ($this->f);
}
function show () {
echo $this->rand_str();
}
}
$f = new reader ('data.txt');
$t1 = time ();
$f->show();
$t2 = time ();
echo '<br>'.($t2-$t1).'c.';
echo '<br>'.memory_get_usage(true);
?>
Две последних строки вывода скрипта:
12c. 786432
Видно, что наличие класса не привело к росту времени выполнения или увеличению объёма используемой памяти. Но даже если бы немного привело, преимущества класса всё равно перекрывают это :)
И, конечно, в реальных скриптах такие объёмы данных нужно читать из базы, а никак не из "плоского" файла.
12.05.2014, 22:16 [10222 просмотра]

