БлогNot. Ещё раз о чтении файла на PHP...

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


теги: программирование ошибка random php

К этой статье пока нет комментариев, Ваш будет первым