• XSS.stack #1 – первый литературный журнал от юзеров форума

Как быстро делать merge нескольких файлов?!

ULTRA

(L3) cache
Пользователь
Регистрация
21.10.2005
Сообщения
154
Реакции
0
Суть проста: слить несколько отсортированных файлов в один. Реализация на php. Как вам кажеться это оптимальный алгоритм?

<?
$arr_files = glob ("splited/*");
$handles = array ();
foreach ($arr_files as $read_file)
{
$handles[] = fopen ($read_file, "rb");
}



$fh = fopen ("sorted_merge.txt", "w");

$arr_str = array ();
//$first_prog = 1;
$handles_buf = array ();
$counter = 0;
$old_counter = 0;

while (1 == 1)
{
$first = 0;
reset ($handles);
while (list ($k, $v) = each ($handles))
{
if (!isset ($handles_buf[$k])
or (@$handles_buf[$k]['counter'] > @$handles_buf[$k]['last']))
{
if (feof ($handles[$k]))
{
echo "\nFEOF {$arr_files[$k]}\n";
fclose ($handles[$k]);
unset ($handles[$k]);
unset ($handles_buf[$k]);
continue;
}
else
{
echo "\nread {$arr_files[$k]}\n";
unset ($handles_buf[$k]);
$buf = fread ($handles[$k], 10000000);
$buf .= fgets ($handles[$k]);
$handles_buf[$k]['arr'] = explode ("\n", $buf);
$handles_buf[$k]['counter'] = 0;
$handles_buf[$k]['last'] =
count ($handles_buf[$k]['arr']) - 1;
}
}

if ($first == 0)
{
$first = 1;
$first_num = $k;
}
else
{
if (strcmp
($handles_buf[$first_num]['arr']
[$handles_buf[$first_num]['counter']],
$handles_buf[$k]['arr'][$handles_buf[$k]['counter']]) > 0)
{
$first_num = $k;

}
}
}
if ($first == 0)
{
die;
}
fwrite ($fh,
$handles_buf[$first_num]['arr'][$handles_buf[$first_num]
['counter']]."\n");
$handles_buf[$first_num]['counter']++;
if ($counter - $old_counter > 10000)
{
echo "\r".$counter." $first_num ".
$handles_buf[$first_num]['arr'][$handles_buf[$first_num]
['counter']];
$old_counter = $counter;
}
$counter++;
}
?>
 
К сожалению, этот не алгоритм, а код, который получается если пару раз удариться фейсом об кейбоард.

Немного не понял по заданию - файлы уже отсортированы? Или их предстоит отсортировать и слить вместе?

Во-первых, я бы советовал обходить директорию с целью получения имён файлов через функции opendir, readdir и closedir. В прочем, если размер файлов не превышает 1024 Кб. в весе, то гораздо проще распарсить их на строки с помощью функции file.

Запись можно реализовать как через функции fopen, fwrite, fgets, fclose, так и через банальный file_put_contents + implode.

Да и вообще, можно было бы банально сделать что-то типо:

Код:
eval('type somefile1.txt somefile2.txt somefile3.txt > somefileX.txt')

для платформы WINDOWS, или:

Код:
eval('cat somefile1.txt somefile2.txt somefile3.txt > somefileX.txt')

для платформы Unix.

В общем ответьте насчёт сортировки, размеров файлов и я вам напишу своё решение...
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Аналогично, интересует отсортированы ли строки в файлах которые нужно соединить ?
И в каком виде должен быть файл на выходе ? Тоже с отсортироваными строками, или тупым соединением исходных файлов типа...
<?php

$files = glob("splited/*");

$fh = fopen("sorted_merge.txt", "w");

while(current($files)) {
echo "processing file: ".current($files)."
";
flush();

$f = fopen(current($files), "r");
while($buff=fread($f, 1024*1024)) fwrite($fh, $buff);
fclose($f);

next($files);
}

fclose($fh);

echo "
<b>WORK COMPLEETE</b>";

?>
?
 
...прочитайте код и вдумайтесь что он делает...
Лично мне неприятно читать такой код, который не пойми как отформатирован, крутится в бесконечном цикле и вообще криво как-то написан...

Я бы этот пример сделал примерно так:

Код:
<?php

	define('RESULT_FILE_PATH', 'result.txt');
	define('TARGET_DIRECTORY_PATH', '.');

	$target_files = array();
	$total_lines = array();

	if (is_dir(TARGET_DIRECTORY_PATH)) {
  if ($dh = opendir(TARGET_DIRECTORY_PATH)) {
  	while (($file = readdir($dh)) !== false) {
    if ($file != "." && $file != ".." && $file != basename(__FILE__))
            	$target_files[] = $file;
  	}
  	closedir($dh);
  }

  $output_file_id = fopen(RESULT_FILE_PATH, 'w') or die('Error: could not open result file!');

  chdir(TARGET_DIRECTORY_PATH);

  foreach($target_files as $target_file) {
  	$input_file_id = fopen($target_file, 'rb') or die("Error: could not open input file {$target_file}!");

  	while (!feof($input_file_id)) {
    $line = fgets($input_file_id);
    $line = trim($line);

    if ($line != '')
    	$total_lines[] = $line;
  	}

  	fclose($input_file_id);
  }

  natsort($total_lines);

  fputs($output_file_id, implode("\x0d\x0a", $total_lines));

  fclose($output_file_id);
	}

?>

В скрипте определены константы:

* RESULT_FILE_PATH - путь к файлу, куда будет скидываться результат.
* TARGET_DIRECTORY_PATH - путь к директории, в которой будут парситься файлы.

Разумеется, нужно следить за размерами файлов, и лимитами памяти, выдаваемыми скрипту в php.ini - иначе может возникнуть ситуация, когда скрипту выделится памяти меньше, чем суммарный размер файлов, которые он парсит. Данный вариант сортирует все строки исходных файлов, перед тем, как сбросить их в результирующий файл. Можно упростить исходник:

Код:
<?php

	define('RESULT_FILE_PATH', 'result.txt');
	define('TARGET_DIRECTORY_PATH', '.');

	$target_files = array();

	if (is_dir(TARGET_DIRECTORY_PATH)) {
  if ($dh = opendir(TARGET_DIRECTORY_PATH)) {
  	while (($file = readdir($dh)) !== false) {
    if ($file != "." && $file != ".." && $file != basename(__FILE__))
            	$target_files[] = $file;
  	}
  	closedir($dh);
  }

  $output_file_id = fopen(RESULT_FILE_PATH, 'w') or die('Error: could not open result file!');

  chdir(TARGET_DIRECTORY_PATH);

  foreach($target_files as $target_file) {
  	$input_file_id = fopen($target_file, 'rb') or die("Error: could not open input file {$target_file}!");

  	while (!feof($input_file_id)) {
    $line = fgets($input_file_id);
    $line = trim($line);

    if ($line != '')
    	fputs($output_file_id, $line . "\x0d\x0a");
  	}

  	fclose($input_file_id);
  }

  fclose($output_file_id);
	}

?>

Второй вариант не требователен к памяти, но просто сливает файло в один файл (без сортировки).
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх