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 гигов обработалось где-то за час.
В результате, мы получили нормальный дамп базы, который потом, причмокивая от удовольствия, употребил постгрес.
Всё.
