Ещё раз о чтении файла на 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 [9901 просмотр]