Работа с большими файлами силами PHP

metrik
Доброго времени суток.
Дано: текстовый файл большого размера (от 100 мб например).
Задача: например заменить, удалить, дописать например где нибудь в середине файла.

Вопрос выглядит просто, но до сих пор не нашел изящного решения.

Какие пути решения были предприняты:
  1. Использовать классические методы работы через file, file_get_contents и т.п.;
  2. Использовать команду оболочки , например "grep" (хотя это уже не совсем по теме);
  3. Использовать второй файл в качестве буфера, в который можно формировать новое готовое состояние исправляемой информации.
Итого по пунктам:
  1. работает медленно и съедает очень много памяти - не вариант;
  2. Не вариант, по той причине, что код может выполняться и на "виндовой" машине. Работает быстро, использовать получается только на удаление строк.
  3. Файл читаю построчно через feof. Поиск данных проводится более менее с приемлемой скоростью и память не ест как в случае с п.1. Но зато рождает второй большой файл.. и если изменений много и они не последовательны , то опять сам процесс очень долгий по продолжительности
Кто что посоветует, кто сталкивался?
Вернуться к началу

Distructor
Администратор
Как вариант, если структура файла позволяет, то хранить его данные в СУБД.
Но если обязательно в файлах, то имхо лучше средствами ос делать

для примера
  • замена подстроки в файле
    • для linux

      Код: Выделить всё

      sed -i 's/что ищем/на что меняем/' /path/source.file
    • для windows (bat-файл)

      Код: Выделить всё

      @echo off
      setlocal enabledelayedexpansion 
      Set infile=source.file
      set outfile=result.file
       
      :: "что ищем" "на что меняем"
      call :1 "PROG_DIR" "DIR_PROG"
      call :1 "PROG_NAME" "NAME_PROG"
      exit /b
       
      :1
      for /f "tokens=1* delims=]" %%a in ('find /v /n "" ^<"%infile%"') do (
          if not "%%b"=="" (set line=%%b) else (set line=not)
          set newline=!line:%~1=%~2!
          set newline=!newline:not=!
          echo.!newline!>>%outfile%
      )
      set infile=%outfile%
      goto :eof
  • удаление (ищем целую строку, соответствующую искомой)
причем sed есть и под windows (http://gnuwin32.sourceforge.net/packages/sed.htm) // Описание использования
Вернуться к началу

metrik
Distructor писал(а):Как вариант, если структура файла позволяет, то хранить его данные в СУБД.
Но если обязательно в файлах, то имхо лучше средствами ос делать
...
причем sed есть и под windows
Спасибо за ответ, приму к сведению. Наверное лучшее решение тогда действительно перегонять данные в БД.
Хотя это опять же не является изящным решением (требует лишних манипуляций по перегону информации в БД, а в остальных случаях стороннего ПО) и не отвечает на вопрос поставленный в заголовке и описании ("работа с большими файлами силами ПХП").
Вернуться к началу

Distructor
Администратор
metrik писал(а): и не отвечает на вопрос поставленный в заголовке и описании ("работа с большими файлами силами ПХП").

Код: Выделить всё

<?php

$result = exec("sed -i 's/что ищем/на что меняем/' /path/source.file"); 
кстати так и не упоминалось, что за данные в файле?
если нормальный csv, то в MySQL можно импортировать одной командой

Код: Выделить всё

LOAD DATA INFILE '/tmp/source.txt' INTO TABLE test FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'; 
обратная операция делается аналогично

Код: Выделить всё

SELECT * INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n' FROM test; 
если нужно решение без использования вызова внешних программ и использования БД, то вариант с feof оптимальный.
Вернуться к началу

metrik
Вот и работаю по 3му варианту. Хочу немного удивился, что нет в PHP функций для внесения или замены данных в файле по смещению указателя
Distructor писал(а):кстати так и не упоминалось, что за данные в файле?
Идентификаторы и цены с разделителем
Вернуться к началу

Distructor
Администратор
metrik писал(а):Идентификаторы и цены с разделителем
стоит попробовать вариант с БД.
импорт, актуализация и экспорт должны выполняться как минимум раз в 10 быстрее чем вариант с чтением файла.

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