Рубрики
Uncategorized

PHP сверхнизкий объем памяти для обхода файла каталога и чтения сверхбольшого файла методом

Автор оригинала: David Wong.

Это не учебник, это заметка, поэтому я не буду систематически обсуждать принцип и реализацию, просто простое объяснение и примеры.

Предисловие

Причина, по которой я пишу эту заметку, заключается в том, что учебные пособия и примеры кодов о том, как PHP просматривает файлы каталогов и читает текстовые файлы PHP в Интернете, крайне неэффективны. Даже если они неэффективны, некоторые из них даже эффективны, что действительно круто.

Эта заметка в основном решает следующие проблемы:

Как PHP может быстро перемещаться по десяткам тысяч файлов каталогов со сверхнизким объемом памяти?

Как PHP может использовать сверхнизкую память для быстрого чтения файлов размером в сотни МБ или даже ГБ?

Кстати, я забыл, что могу искать свои собственные заметки через поисковую систему. (потому что очень редко можно написать эти две функции на PHP. У меня плохая память, поэтому я не забываю сделать еще один крюк.)

Файл каталога обхода

В Интернете большинство примеров кодов для реализации этого метода-комбинация glob или opendir + readdir, что не является проблемой, когда файлов каталогов немного, но возникает проблема, когда файлов много (это относится ко времени, когда функция инкапсулируется для равномерного возврата массива), слишком большой массив потребует использования большой памяти, что не только приводит к медленной скорости, но и напрямую, когда недостаточно памяти, она сворачивается.

В настоящее время правильным методом реализации является использование ключевого слова yield для возврата. Вот мой последний код:

php

function glob2foreach($path, $include_dirs=false) {
  $path = rtrim($path, '/*');
  if (is_readable($path)) {
    $dh = opendir($path);
    while (($file = readdir($dh)) !== false) {
      if (substr($file, 0, 1) == '.')
        continue;
      $rfile = "{$path}/{$file}";
      if (is_dir($rfile)) {
        $sub = glob2foreach($rfile, $include_dirs);
        while ($sub->valid()) {
          yield $sub->current();
          $sub->next();
        }
        if ($include_dirs)
          yield $rfile;
      } else {
        yield $rfile;
      }
    }
    closedir($dh);
  }
}

// use
$glob = glob2foreach('/var/www');
while ($glob->valid()) {
  
  //Current file
  $filename = $glob->current();
  
  //This is the full filename including the path
  // echo $filename;

  //Point to the next, not less
  $glob->next();
}

Yield возвращает объект генератора (если вы не знаете, вы можете сначала узнать о генераторе PHP). Он не генерирует массивы сразу, поэтому независимо от того, сколько файлов находится в каталоге, огромного массива не будет. Потребление памяти составляет всего лишь незначительный уровень в десятки КБ, а потребление времени почти циклическое.

Чтение текстового файла

Чтение текстового файла похоже на просмотр файла каталога. Онлайн-учебники в основном используют содержимое файла get для чтения в память или fopen + feof + fgetc для чтения и использования. Можно обрабатывать небольшие файлы, но этого недостаточно для обработки больших файлов. Использование содержимого файла для чтения сотен мегабайт файлов-это почти самоубийство.

Правильный способ решения этой проблемы также связан с ключевым словом yield, которое может быть обработано строка за строкой через yield, или объект splfileobject может быть прочитан из указанного местоположения.

Прочитайте весь файл строка за строкой:

valid()) {
  
  //Current line text
  $line = $glob->current();
  
  //Process data line by line
  // $line

  //Point to the next, not less
  $glob->next();
}

Прочитайте файл строка за строкой через yield. Объем используемой памяти зависит от объема данных в каждой строке. Если файл журнала содержит всего несколько сотен байт в каждой строке, даже если размер файла превышает 100 м, используемая память составляет всего КБ.

Но часто нам не нужно читать весь файл сразу. Например, когда мы хотим прочитать файл журнала 1g на страницах, мы можем захотеть прочитать первые 1000 строк на первой странице и вторые от 1000 до 2000 строк на второй странице. В настоящее время мы не можем использовать описанный выше метод, потому что, хотя этот метод занимает меньше памяти, требуется время для десятков тысяч циклов.

В этом случае вместо этого используется splfileobject. Splfileobject можно прочитать из указанного количества строк. В следующем примере необходимо записать возвращаемый массив. Вы можете решить, писать массив или нет в соответствии с вашим бизнесом. Я слишком ленив, чтобы менять его.

seek($offset); 

  $i = 0;
  
  while (! $fp->eof()) {
    
    //Must be at the beginning
    $i++;
    
    //Read only $count so many lines
    if ($i > $count)
      break;
    
    $line = $fp->current();
    $line = trim($line);

    $arr[] = $line;

    //Point to the next, not less
    $fp->next();
  }
  
  return $arr;
}

Все вышеперечисленное относится к случаям, когда файл огромен, но объем данных в каждой строке невелик. Иногда это не так. Иногда в одной строке находятся сотни МБ данных. Как с этим бороться?

Если это так, то это зависит от конкретного бизнеса. Splfileobject может найти позицию символа с помощью fseek (обратите внимание, что она отличается от поиска по количеству строк), а затем прочитать указанную длину символов с помощью free.

То есть, fseek и freed могут считывать сверхдлинную строку по разделам, то есть могут реализовывать сверхнизкую обработку памяти, но как это сделать, зависит от конкретных бизнес-требований, позволяющих вам это сделать.

Копирование больших файлов

Кстати, в PHP можно копировать небольшие файлы с функцией копирования. Лучше копировать большие файлы с потоком данных, например:

Последний

Я просто говорю, что вывод состоит в том, что данные теста не показаны, что может быть трудно убедить общественность. Если вы настроены скептически и хотите это доказать, вы можете использовать режим пикового использования памяти и микротайм для проверки занятой памяти и времени выполнения кода.

Выше приведено все содержание этой статьи. Я надеюсь, что это поможет вам в вашем исследовании, и я надеюсь, что вы сможете больше поддерживать developepaer.