!null.com
23
09
2008

Iconv, конвертирование больших файлов

Привет

Хочу рассказать о простом решении сложной проблемы.

Началось всё с того, что я захотел проапгрейдить PostgreSQL с версии 8.2 до версии 8.3 на сервере

Казалось бы ничего сложного. Сделал pg_dump всей базы, установил новую версию постгреса, потом pg_restore. Но не тут-то было. Видно, что команда разработчиков постгреса, специально старается, чтобы жизнь у нас была не такой скучной :) Pg_restore при востановлении базы выдаёт: "could not execute query: ERROR: invalid byte sequence for encoding UTF8...". Тоесть недвусмысленно намекает, что в дамп базы, попали символы которые не то что в базе, а вроде как и в природе (в UTF8 кодировке) не должны быть.

Ну да ладно. Поверим на слово, и попытаемся исправить ситуацию.

Есть в линуксе такая библиотека, iconv называется. Предназначенна для конвертации из одной кодировки в другую. Помимо конвертации она обладает ещё одним полезным качеством - умеет вырезать символы которые не входят в заданную кодировку. То есть как раз делает то, что нам необходимо.

Недолго думаю пишем в консоли: iconv -c old_dump.sql new_dump.sql

и... получаем опять сообщение об ошибке: "cannot open input file 'old_dump.sql': Value too large for defined data type".

Хм... видимо иконву не нравится что дамп базы на 15 гигов :)

Попробуем обмануть его. Так как гугл ничего не подсказал, пишем простенький PHP скрипт: 

// На всякий пожарный, выделяем больше памяти.
ini_set('memory_limit', '250M');

// Два входных параметра. Первый — имя входящего файла, 
// второй — имя результирующего файла
$fr = fopen($argv[1], 'r');
$fw = fopen($argv[2], 'w');

echo 'Repair file '.$argv[1]."\n";
echo 'Write to file '.$argv[2]."\n";

$m = 0;

// Бежим, строго по строкам файла
while (!feof($fr))
{
    $m++;
    $stroka1 = fgets($fr);
    // вырезаем «плохие» символы
    $stroka2 = StripBadUTF82($stroka1);
    
    // Если что-то вырезалось, то выводим в консоль обе строки
    if ($stroka1!=$stroka2)
    {
    	echo 'Read line: '.$m."\n";
        	echo 'Founded BAD UTF8 on line: '.$stroka1."\n";
        	echo 'Converted to: '.$stroka2."\n";
    }
        
    // пишем в файл
    if (fwrite($fw, $stroka2) === FALSE)
    {
        	echo 'Cannot write to file!'; die();
    }
	
}

fclose($fr);
fclose($fw);

function StripBadUTF82($str)
{
	// IGNORE — говорит что нужно вырезать «плохие» символы
	$str = iconv('UTF-8', 'UTF-8//IGNORE', $str);
     	return $str;
}

 

Вызываем скрипт просто: php iconvadv.php old_dump.sql new_dump.sql > iconv.log

Первый параметр — начальный файл

Второй — конечный файл

iconv.log — логирование процесса

 

Скорость работы скрипта вполне приемлема — 15 гигов обработалось где-то за час.

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


Всё.






Последние записи