Модераторы: skyboy, MoLeX, Aliance, ksnk

Поиск:

Закрытая темаСоздание новой темы Создание опроса
> Чтение/редактирование огромного файла на php, оптимизация и минимизация затрат ресурса 
:(
    Опции темы
Zmiuko
  Дата 27.2.2010, 07:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


http://zmiuko.ru
**


Профиль
Группа: Участник
Сообщений: 297
Регистрация: 8.10.2008

Репутация: 2
Всего: 2



Ребят, у меня проблема.

Все знают как открыть файл на php, как его видоизменить посредством php, как удалить, выставить права и т.д.

Проблема у меня заключается в том, что необходимо прочесть, а точнее выдернуть строку из огромного файла, будь то 100-мегабайтный или гигабайтный файл. И не только выдернуть, но и потом редактировать. Например какая либо cvs база, условно.

Короче, как, каким образом можно оптимизировано и легко прочесть строку в гигабайтном файле на php?

По мне в теории неясно какие функции читают файл в целом, а какие можно использовать для перемещения курсора, таким образом чтобы и строка была найдена-прочитана, да еще чтобы файл целиком не читался и не вызывал остановку скрипта. Плюс то что ограничение в php всего на 8 мегабайт. 

Прошу помощи, так как уже долго маюсь этой проблемой. Не могу разрешить.

Заранее благодарен.

Добавлено через 2 минуты и 47 секунд
Речь идет не об использовании сторонних модулей, либо самописных на стороне сервера, речь идет об обычном php, стандартных функциях.
PM MAIL WWW ICQ Skype Jabber   Вверх
Fortop
Дата 27.2.2010, 07:57 (ссылка) |    (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2200
Регистрация: 13.11.2007
Где: Донецк

Репутация: 20
Всего: 42



Цитата(Zmiuko @  27.2.2010,  06:30 Найти цитируемый пост)
По мне в теории неясно какие функции читают файл в целом, а какие можно использовать для перемещения курсора, таким образом чтобы и строка была найдена-прочитана, да еще чтобы файл целиком не читался и не вызывал остановку скрипта. Плюс то что ограничение в php всего на 8 мегабайт. 

http://php.net/fgets - читать построчно.

А вот изменить файл без полной перезаписи будет много сложнее. (и это зависит от того используются ли равноразмерные строки, или же переменной длины)
Для редактирования равноразмерных строк можно использовать
http://php.net/rewind
http://php.net/fseek
http://php.net/fwrite






--------------------
Мир это Я.
Живее всех живых.
PM MAIL   Вверх
Zmiuko
  Дата 27.2.2010, 08:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


http://zmiuko.ru
**


Профиль
Группа: Участник
Сообщений: 297
Регистрация: 8.10.2008

Репутация: 2
Всего: 2



Но fgets() читает весь полностью файл до указания байта. То есть файл весом в 1,5 Gb мы должны прочитать полностью, к примеру, 1,2 Gb, чтобы найти искомую строку. Любой хостинг, даже с указанием максимально возможного времени выполнения php-скрипта закроет выполнение с ошибкой и указателем на строку, на которой остановилось данное выполнение.

А вот если же мы какую либо базу держим в ассоциативных массивах, условно, для примера, то тут вообще почти будет невозможным совершить какое-либо чтение при достижении файла даже 10 мегабайт?

Нинавижу тестовые задания!

Добавлено через 10 минут и 35 секунд
Задам вопрос по-другому,

к примеру на сайт загружен файл для последущего анализа php-скриптом, в нем,  к примеру много-много сток, около 10-50 тысяч, файл вида:

строка1
строка2
строка3
...
строка12742
строка12743
...
строка54001
строка54002
...
строка100234
строка100235


,около 100235 строк, строки большие, где-то по 1 кб каждая.

Каким образом, посредством классического, пускай процедурного php, без сторонних модулей, возможно выдернуть и изменить строку, скажем 23567-ую ?
PM MAIL WWW ICQ Skype Jabber   Вверх
IZ@TOP
Дата 27.2.2010, 09:40 (ссылка) |  (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Панда-бир!
****


Профиль
Группа: Участник
Сообщений: 4795
Регистрация: 3.2.2003
Где: Бамбуковый лес

Репутация: 44
Всего: 73



Цитата(Zmiuko @  27.2.2010,  09:12 Найти цитируемый пост)
Но fgets() читает весь полностью файл до указания байта.

Если бы вы были внимательны, прочли в документации о том, что

length
Reading ends when length  - 1 bytes have been read, on a newline (which is included in the return value), or on EOF (whichever comes first). If no length is specified, it will keep reading from the stream until it reaches the end of the line. 

Из чего мы делаем вывод, что fgets читает с буфером построчно, где буфер - максимально возможная длинна строки в файле.
Совершенно не важно какого размера файл, подобная конструкция прочтет его целиком не поперхнувшись:

Код

fopen($file, 'r');  
while($string = fgets($file, 2048)) { // Читаем построчно с буфером в 2 кб
    if(($exists = strpos($string, $search))!==false) {
        break;
    } else {
         $bytes += strlen($string); // Определяем смещение в файле, если понадобится последующее его изменение
    }
    $le++; // Обновляем номер строки
}


И т.п., надеюсь из этого примера ясно, что с чем надо потреблять?


--------------------
Один из розовых плюшевых-всадников апокалипсиса... очень злой...

Семь кругов ада для новых элементов языка
Мои разрозненные мысли
PM MAIL WWW ICQ Skype GTalk   Вверх
Zmiuko
  Дата 27.2.2010, 10:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


http://zmiuko.ru
**


Профиль
Группа: Участник
Сообщений: 297
Регистрация: 8.10.2008

Репутация: 2
Всего: 2



Ну, тогда извиняюсь.

Проблему чтения файла большого размера я еще решил подобным образом:

1) читаем файл по кускам...
2) кусками сохраняем во временный файл...
3) если во временном файле искомого не обнаружено... то стираем временный файл и приступаем к чтению и сохранению следующего куска...


за 24 секунды выводит строку из 300mb файла

Но как быть с редактированием, тут сложнее, или возможно также делить кусками, что вполне реально и при нахождении строки заменять ее, а затем куски "склеивать" ?
PM MAIL WWW ICQ Skype Jabber   Вверх
Zmiuko
Дата 27.2.2010, 11:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


http://zmiuko.ru
**


Профиль
Группа: Участник
Сообщений: 297
Регистрация: 8.10.2008

Репутация: 2
Всего: 2



 smile 
PM MAIL WWW ICQ Skype Jabber   Вверх
Ипатьев
Дата 27.2.2010, 12:43 (ссылка) |  (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2232
Регистрация: 5.7.2009

Репутация: 28
Всего: 37



Цитата(Zmiuko @  27.2.2010,  10:02 Найти цитируемый пост)
Но как быть с редактированием

Наверное, ставить задачу так, чтобы при ее решении не требовалось оптимизированое редактинование стогигабайтного файла?
PM MAIL   Вверх
Zmiuko
  Дата 27.2.2010, 12:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


http://zmiuko.ru
**


Профиль
Группа: Участник
Сообщений: 297
Регистрация: 8.10.2008

Репутация: 2
Всего: 2



Я просто ищу какое-либо решение и выход из поставленной ситуации. Пока не могу придумать.
PM MAIL WWW ICQ Skype Jabber   Вверх
Ипатьев
Дата 27.2.2010, 13:07 (ссылка) |  (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2232
Регистрация: 5.7.2009

Репутация: 28
Всего: 37



А не надо ничего придумывать.
Если кто-то поставит задачу возить на велосипеде пятитонные металлоконструкции, то надо не думать над решением. А думать над умственным здоровьем постановщика задачи. И искать более адекватного.

С какой стати возник вопрос об оптимальной записи? Какая разница, будет она делаться 5 минут или 25?

Добавлено через 1 минуту и 14 секунд
Повторю, если непонятно.
Если задача ставится заведомо нерешаемая, то не нужно думать над ее решением.
Надо думать над ее изменением.

PM MAIL   Вверх
Zmiuko
  Дата 27.2.2010, 13:49 (ссылка) |  (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


http://zmiuko.ru
**


Профиль
Группа: Участник
Сообщений: 297
Регистрация: 8.10.2008

Репутация: 2
Всего: 2



Хм. Но коль мне необходимо решить эту проблему, хочу я этого или нет. К чему философия?
PM MAIL WWW ICQ Skype Jabber   Вверх
Ипатьев
Дата 27.2.2010, 13:56 (ссылка)  | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2232
Регистрация: 5.7.2009

Репутация: 28
Всего: 37



К тому, что "необходимо" - это заблуждение, разумеется.

PM MAIL   Вверх
IgorIV
Дата 27.2.2010, 20:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 642
Регистрация: 7.9.2009

Репутация: 2
Всего: 8



А это случайно не видеофайл? smile
PM MAIL   Вверх
gcc
Дата 27.2.2010, 23:18 (ссылка)    | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Агент алкомафии
****


Профиль
Группа: Участник
Сообщений: 2691
Регистрация: 25.4.2008
Где: %&й

Репутация: -1
Всего: 17



 smile  "бинарный поиск" тут нужно преминить?

Это сообщение отредактировал(а) gcc - 27.2.2010, 23:19
PM WWW ICQ Skype GTalk Jabber   Вверх
gcc
Дата 27.2.2010, 23:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Агент алкомафии
****


Профиль
Группа: Участник
Сообщений: 2691
Регистрация: 25.4.2008
Где: %&й

Репутация: -1
Всего: 17



Цитата(Zmiuko @ 27.2.2010,  10:02)
Ну, тогда извиняюсь.

Проблему чтения файла большого размера я еще решил подобным образом:

1) читаем файл по кускам...
2) кусками сохраняем во временный файл...
3) если во временном файле искомого не обнаружено... то стираем временный файл и приступаем к чтению и сохранению следующего куска...


за 24 секунды выводит строку из 300mb файла



если строки упорядоченные, отсортированные, так как вы написали, то нужно использовать "Двоичный_поиск"
http://ru.wikipedia.org/wiki/%D0%94%D0%B2%...%B8%D1%81%D0%BA

и друугие методы математические http://ru.wikipedia.org/wiki/%D0%9C%D0%B5%...%86%D0%B8%D0%B8

из wikipedia для бинарного:
Код

  i := 1;  { индекс первого элемента массива }
  j := n;  { индекс последнего элемента массива (0, если массив пуст) }
 
  while i <= j do begin
    { ищем элемент на интервале индексов от i до j, включительно }
    k := i + (j - i) div 2;  { k = элемент посередине интервала }
    if x > A[k] then
      i := k + 1
    else if x < A[k] then
      j := k - 1
    else
      break;  { искомый элемент найден }
  end;
 
  { алгоритм выше останавливается в двух случаях:
    1) либо i > j и элемент не был найден,
    2) либо i <= k <= j и A[k] = искомый элемент. }



Код

#!/usr/bin/perl

  if (!$ARGV[0])   {
    print 'no key';
    exit;    }

  open my $fi, '666.txt';

  sub filess {
    my $ss = int((@_[0] + @_[1]) / 2);
      return 0 if @_[0] == $ss or @_[1] == $ss;
    seek $fi, $ss, 0;
    <$fi>;
    my $line = <$fi>;

    my @h = split( /\t/, $line, 2 );
   if ($h[0] eq $ARGV[0]) {
    print $h[1];
    exit;
   }
    return filess(@_[0], $ss) if(($ARGV[0] cmp $h[0]) == -1);
    return filess($ss, @_[1]) if(($ARGV[0] cmp $h[0]) == 1);
  }

  filess(0, -s $fi);



Код

#!/usr/bin/perl

use File::SortedSeek ':all';

@{$hex->[0]} = 'D';

  open BIG, 'file.txt' or die $!;

$tell = alphabetic( *BIG, @{$hex->[0]} );
$line = <BIG>;

@{$hex->[1]} = split( /\t/, $line, 2 );

print @{$hex->[1]};


Код

#!/usr/bin/perl -w

# размер блока для чтения, чтобы не читать посимвольно
my $read_block_size = 256;

# функция для сдвига к границе записи
# принимает хэндл и направление движения
# направление = 0 - двигаемся вперед к началу следующей записи - вообще-то не используется smile.gif
# = 1 - назад - к началу текущей записи
sub rec_shift {
my( $file, $dir ) = @_;
my( $buf, $ofs );
# крутимся пока не дойдем
while( 1 ) {
# получаем текущее смещение
$ofs = tell( $file );
# в зависимости от направления
if( !$dir ) {
# направление вперед, аналогично считыванию записи,
# к тому же не используется в программе, коментировать лень
read( $file, $buf, $read_block_size );
my $o = index( $buf, "\x0a" );
if( $o >= 0 ) {
seek( $file, $ofs + $o + 1, 0 );
return;
} elsif( eof( $file ) ) {
return;
}
} else {
# к началу записи - назад
# смотрим на текущее смещение и сравниваем с размером блока который хотим считать
# ессно, выбираем меньшее
my $r = $ofs > $read_block_size ? $read_block_size : $ofs;
# смещаемся назад на размер блока
seek( $file, -$r, 1 );
# читаем блок
read( $file, $buf, $r );
# снова возвращаемся на начало блока, т.к. чтение сдвинуло на прежнюю точку
seek( $file, -$r, 1 );
# ищем разделитель (ессно, с конца блока, потому что идем назад)
my $o = rindex( $buf, "\x0a" );
if( $o >= 0 ) {
# нашли! смешаемся на начало записи и все smile.gif
seek( $file, $o + 1, 1 );
return;
} elsif( $r == $ofs ) {
# не нашли, но размер блока для чтения равен ранее смещению до конца текущего блока
# следовательно мы находимся в начале файла (а значит и какой-то записи)
# можно было не сдвигаться, но так, на всякий пожарный smile.gif
seek( $file, 0, 0 );
return;
}
}
}
}

# считывает запись от текущего положения и смещает к началу следующей
# АХТУНГ! функция преполагает, что мы находимся в начале записи
# принимает хэндл файла
sub rec_read {
my( $file ) = @_;
# 'это и будет наша запись
my $ln = '';
my $buf;
while( 1 ) {
# получаем текущее смещение
my $ofs = tell( $file );
# считываем блок
read( $file, $buf, $read_block_size );
# ищем в нем перевод стоки - разделитель записей
my $o = index( $buf, "\x0a" );
# если нашли, то
if( $o >= 0 ) {
# смещаемся к началу следующей записи
seek( $file, $ofs + $o + 1, 0 );
# добавляем кусок буфера от начала до конца (не включая разделитель)
$ln .= substr( $buf, 0, $o );
# разделяем строку по табуляции (разделитель ключа и значения) и возвращаем такой вот массивчик
return split( /\t/, $ln );
}
# если не нашли разделитель --> добавляем весь буфер и читаем следующую порцию, повторяя все снова, пока не найдем
$ln .= $buf;
}
}

# основная функция бинарного поиска
# принимает хэндл файла, ключ, начало и конец данных для поиска
sub filebinsearch {
my( $file, $fkey, $beg, $end ) = @_;
# если конец == началу, то данных уже нет, значит ключ не найден
return undef if $beg == $end;
# вычисляем середину наших данных
my $oc = int( ( $beg + $end ) / 2 ); # ( $beg + $end ) >> 1 -- 32bit :-(
# смещаемся к середине
seek( $file, $oc, 0 );
# сдвигаемся к НАЧАЛУ записи (могли попасть куда угодно) на которой стоим
rec_shift( $file, 1 );
# запоминаем смещение начала файла
$oc = tell( $file );
# считываем ценральную запись
my( $key, $value ) = rec_read( $file );
# если попали туда, куда надо - возвращаем значение
return $value if $key eq $fkey;
# сравниваем эталонный ключ с найденным
if( $key lt $fkey ) {
# найденный ключ меньше эталонного, значит первую половину
# можно отбросить и повторить поиск только для второй части
# которая начинается с конца ранее найденной и заканичвается там,
# где и был конец исходных данных
filebinsearch( $file, $fkey, tell( $file ), $end );
} else {
# найденный ключ больше искомого --> опускаем вторую половину
# ищем только в первой, кот. начинается с начала исходных
# данных и заканчивается в начале ранее найденной записи
filebinsearch( $file, $fkey, $beg, $oc );
}
}


# собственно функция задания, которую и надо вызывать
# принимает имя файла и ключ который ищем
sub findinfile {
my( $filename, $key ) = @_;
my $value = undef;
my $filesize = 0;

# а есть ли вообще такой файл?
return undef if !-f $filename;
# а можем ли мы его прочесть?
return undef if !-r $filename;
# а в нем хоть что-то есть? попутно сохраняем размер файла
return undef if !($filesize = -s $filename);
# открываем и проверяем получилось ли это
return undef if !open( $file, '<', $filename );
# вызываем рекурсивную функцию - основа бинарного поиска
return filebinsearch( $file, $key, 0, $filesize );
}

print findinfile( $ARGV[0], $ARGV[1] ) . "\n";



поиск по файлу в 20Гиг будет не более чем 2-3 сек. и не будут использоватся ресурсы....

Цитата(Zmiuko @ 27.2.2010,  10:02)


Но как быть с редактированием, тут сложнее, или возможно также делить кусками, что вполне реально и при нахождении строки заменять ее, а затем куски "склеивать" ?


чтобы добавить в конец, можно использовать не буферизированные Сишные  функции syswrite и sysread чтобы добавить в файл не открывая весь файл

Это сообщение отредактировал(а) gcc - 28.2.2010, 04:49
PM WWW ICQ Skype GTalk Jabber   Вверх
IZ@TOP
Дата 1.3.2010, 11:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Панда-бир!
****


Профиль
Группа: Участник
Сообщений: 4795
Регистрация: 3.2.2003
Где: Бамбуковый лес

Репутация: 44
Всего: 73



gcc, это было круто, однако у человека задача сделать сие на PHP, а поскольку задача скорее всего учебная, то другой язык не подойдет.


--------------------
Один из розовых плюшевых-всадников апокалипсиса... очень злой...

Семь кругов ада для новых элементов языка
Мои разрозненные мысли
PM MAIL WWW ICQ Skype GTalk   Вверх
Закрытая темаСоздание новой темы Создание опроса
Правила форума "PHP"
Aliance
IZ@TOP
skyboy
SamDark
MoLeX

Новичкам:

  • PHP редакторы собираются и обсуждаются здесь
  • Электронные книги по PHP, документацию можно найти здесь
  • Интерпретатор PHP, полную документацию можно скачать на PHP.NET

Важно:

  • Не брезгуйте пользоваться тегами [code=php]КОД[/code] для повышения читабельности текста/кода.
  • Перед созданием новой темы воспользуйтесь поиском и загляните в FAQ
  • Действия модераторов можно обсудить здесь

Внимание:

  • Темы "ищу скрипт", "подскажите скрипт" и т.п. будут переноситься в форум "Web-технологии"
  • Темы с именами: "Срочно", "помогите", "не знаю как делать" будут УДАЛЯТЬСЯ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, IZ@TOP, skyboy, SamDark, MoLeX, awers.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | PHP: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0704 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.