В моей задаче необходимо было сохранять таблицу значений в файл. Но при большой нагрузке на сервер приложений таблица не сериализировалась через ЗначениеВСтрокуВнутр.
Методика, предложенная в 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";
ТД = САП.ТЗВТекстЧерезСтроки(ТЗ);
Сообщить(ТД.ПолучитьТекст());
ТЗ = САП.ТЗИзТекстаЧерезСтроки(ТД);
ТЗ.ВыбратьСтроку();
А какова разница по скорости упаковки/распаковки обычного метода и построчного на больших таблицах? Если не очень существенная, то я бы не давал шанса обычному методу вывалить ошибку о нехватке памяти (если эта ошибка конечно не быстро вываливается). Или все же имеет смысл разделять использование методов в зависимости от ситуации?
(1) khaoos, осмелюсь предположить, что разница в скорости будет в несколько раз.
(2) не замерял, но думаю, что стандартный метод работает гораздо быстрее
Имхо, чем выгружать построчно, да еще перебирая все колонки в цикле, можно используя метод Скопировать ТаблицыЗначений, выдергивать по 1000 строк в другую ТЗ, и сохранять ее.
(4) да, согласен, будет работать быстрее, но мне нужно было сделать затычку для защиты от переполнения памяти. Переделывать уже не буду.