Сериализация больших таблиц значений в 1С8

Рассмотрена специфика и предложена конкретная методика сериализации в файл больших таблиц значений
Введение в проблему

В моей задаче необходимо было сохранять таблицу значений в файл. Но при большой нагрузке на сервер приложений таблица не сериализировалась через ЗначениеВСтрокуВнутр.

Методика, предложенная в http://kb.mista.ru/2/doku.php?id=1c:v8:howto:serializacija_tablicyznachenij_v_xml, не работает в 1с81 (8.1.15.14), видимо,  в более ранних версиях платформы это не возможно.

Если упаковать таблицу значений в хранилище значений, то методика работает:
ХЗ = Новый ХранилищеЗначений(ТЗ);

Однако, проблема заключалась в том, что при критической нагрузке и в хранилище значений 1С не могла упаковать таблицу значений, выдавала ошибку о нехватке памяти.

Решение

Решение нужно было написать быстро, и скорость чтения таблиц не должна была бы сильно пострадать. Скорость записи была не принципиальна.

Решено было таблицу пробовать выгружать обычным методом, а если ЗначениеВСтрокуВнутр выдаст ошибку, выгружать построчно.

Соответственно, при восстановлении таблицы проверялся формат файла, и в зависимости от формата использовалась та или иная распаковка.

 

Код по упаковке в модуле САП:

 

Функция ТЗВТекст(ТЗ) Экспорт

Попытка

Т = Новый ТекстовыйДокумент();

Т.УстановитьТекст(ЗначениеВСтрокуВнутр(ТЗ));

Возврат Т;

Исключение

Возврат ТЗВТекстЧерезСтроки(ТЗ);

КонецПопытки;

КонецФункции



Функция ТЗИзТекста(ТекстовыйДокумент) Экспорт



Если ТекстовыйДокумент.ПолучитьСтроку(1) = "LINE_FORMAT" Тогда

Возврат ТЗИзТекстаЧерезСтроки(ТекстовыйДокумент);

КонецЕсли;



Возврат ЗначениеИЗСтрокиВнутр(ТекстовыйДокумент.ПолучитьТекст());

КонецФункции



Функция ТЗВТекстЧерезСтроки(ТЗ) Экспорт

Т = Новый ТекстовыйДокумент();

МассивКолонок = Новый Массив();

ВсегоКолонок = ТЗ.Колонки.Количество()-1;

Для Инд = 0 По ВсегоКолонок Цикл

МассивКолонок.Добавить(0);

КонецЦикла;



Т.ДобавитьСтроку("LINE_FORMAT");



ТЗ2 = ТЗ.СкопироватьКолонки();



РезСтрока = ЗначениеВСтрокуВнутр(ТЗ2);

РезСтрока = ЭкранироватьСимволы(РезСтрока);

Т.ДобавитьСтроку(РезСтрока);





Для Каждого Строка ИЗ ТЗ Цикл

Для Инд = 0 По ВсегоКолонок Цикл

МассивКолонок[Инд] = Строка[Инд];

КонецЦикла;

РезСтрока = ЗначениеВСтрокуВнутр(МассивКолонок);

РезСтрока = ЭкранироватьСимволы(РезСтрока);

Т.ДобавитьСтроку(РезСтрока);

КонецЦикла;

Возврат Т;

КонецФункции



Функция ТЗИзТекстаЧерезСтроки(ТекстовыйДокумент) Экспорт

ИсхСтрока = РазЭкранироватьСимволы(ТекстовыйДокумент.ПолучитьСтроку(2));

ТЗ = ЗначениеИзСтрокиВнутр(ИсхСтрока);

ВсегоКолонок = ТЗ.Колонки.Количество()-1;

Для Инд = 3 По ТекстовыйДокумент.КоличествоСтрок() Цикл

ИсхСтрока = РазЭкранироватьСимволы(ТекстовыйДокумент.ПолучитьСтроку(Инд));

Массив = ЗначениеИзСтрокиВнутр(ИсхСтрока);

НСтр = ТЗ.Добавить();

Для КолИнд = 0 По ВсегоКолонок Цикл

НСтр[КолИнд] = Массив[КолИнд];

КонецЦикла;

КонецЦикла;



Возврат ТЗ;



КонецФункции



Функция ЭкранироватьСимволы(Строка)

Р = СтрЗаменить(Строка, "", "\");

Р = СтрЗаменить(Р, Символы.ПС, "
");

Р = СтрЗаменить(Р, Символы.ВК, "
");

Р = СтрЗаменить(Р, Символы.Таб, "	");



Возврат Р;

КонецФункции



Функция РазЭкранироватьСимволы(Строка)

Р = СтрЗаменить(Строка, "\", "");

Р = СтрЗаменить(Р, "
", Символы.ПС);

Р = СтрЗаменить(Р, "
", Символы.ВК);

Р = СтрЗаменить(Р, "	", Символы.Таб);

Возврат Р;

КонецФункции

 

Пример использования кода:

Функция СохранитьРасчет(ИмяФайла, Таблица) Экспорт

Попытка

Значение = ЗначениеВСтрокуВнутр(Таблица);

Т = Новый ТекстовыйДокумент();

Т.УстановитьТекст(Значение);

Т.Записать(ИмяФайла, "UTF-8");



Возврат ИмяФайла;

Исключение

ОписаниеОшибки = ОписаниеОшибки();

Сообщить("Не смогли преобразовать таблицу для сохранения в файл: " + ИмяФайла +  "  " + ОписаниеОшибки + ". Будем записывать в другом формате.", СтатусСообщения.Важное);



Т = САП.ТЗВТекст(Таблица);

Т.Записать(ИмяФайла, "UTF-8");

КонецПопытки;



КонецФункции





Функция ВосстановитьРасчет(ИмяФайла) Экспорт

Попытка

Т = Новый ТекстовыйДокумент();

Т.Прочитать(ИмяФайла, "UTF-8");

Значение = САП.ТЗИзТекста(Т);

Исключение

Возврат Неопределено;

КонецПопытки;

Возврат Значение;



КонецФункции

Пример тестирования функции:

 

ТЗ = новый ТаблицаЗначений;

ТЗ.Колонки.Добавить("Кол1");

ТЗ.Колонки.Добавить("Кол2");



Стр = ТЗ.Добавить();

Стр.Кол1 = 1;

Стр.Кол2 = "1";



Стр = ТЗ.Добавить();

Стр.Кол1 = 2;

Стр.Кол2 = "2";







ТД = САП.ТЗВТекстЧерезСтроки(ТЗ);

Сообщить(ТД.ПолучитьТекст());

ТЗ = САП.ТЗИзТекстаЧерезСтроки(ТД);

ТЗ.ВыбратьСтроку();


 

5 Comments

  1. khaoos

    А какова разница по скорости упаковки/распаковки обычного метода и построчного на больших таблицах? Если не очень существенная, то я бы не давал шанса обычному методу вывалить ошибку о нехватке памяти (если эта ошибка конечно не быстро вываливается). Или все же имеет смысл разделять использование методов в зависимости от ситуации?

    Reply
  2. ander_

    (1) khaoos, осмелюсь предположить, что разница в скорости будет в несколько раз.

    Reply
  3. fixin

    (2) не замерял, но думаю, что стандартный метод работает гораздо быстрее

    Reply
  4. orefkov

    Имхо, чем выгружать построчно, да еще перебирая все колонки в цикле, можно используя метод Скопировать ТаблицыЗначений, выдергивать по 1000 строк в другую ТЗ, и сохранять ее.

    Reply
  5. fixin

    (4) да, согласен, будет работать быстрее, но мне нужно было сделать затычку для защиты от переполнения памяти. Переделывать уже не буду.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *