При работе с платформой 1С:Предприятие 7.7 разработчики часто сталкиваются с аппаратными и системными ограничениями при обработке больших массивов данных. Одна из наиболее острых проблем — формирование текстовых файлов (например, в формате JSON) объемом в сотни тысяч строк. Попытка собрать такой текст в одну переменную типа Строка неизбежно приводит к падению программы. В этой статье мы подробно разберем, почему это происходит, и рассмотрим эффективные способы решения задачи через потоковую запись.
Проанализируем ситуацию: стандартный механизм работы со строками в 1С 7.7 при использовании оператора сложения (конкатенации) вида Стр = Стр + НоваяСтрока крайне неэффективен. Платформа каждый раз выделяет в оперативной памяти новый непрерывный блок для хранения увеличившейся строки, копирует туда старые данные и добавляет новые. Когда количество строк достигает сотен тысяч (как в примере автора — 185 000 записей), оперативная память фрагментируется, и в какой-то момент система не может найти достаточно большой свободный сегмент. Это вызывает критическую ошибку и закрытие приложения.
Чтобы обойти это ограничение, мы должны отказаться от накопления всего текста в памяти и перейти к потоковой записи данных непосредственно в файл на диске.
Одним из самых простых и доступных способов является использование системного COM-объекта Scripting.FileSystemObject. Он позволяет записывать данные в текстовый файл построчно, не перегружая память 1С.
Разберем по шагам, как реализовать выгрузку данных в JSON через FSO:
Scripting.FileSystemObject.CreateTextFile.WriteLine или Write.Рассмотрим пример кода:
Процедура СформироватьJSON_FSO()
ФС_Объект = СоздатьОбъект("Scripting.FileSystemObject");
// Создаем файл, перезаписывая существующий (True), в кодировке ASCII (False)
ТекстФайл = ФС_Объект.CreateTextFile("C:\data_export.json", -1, 0);
// Записываем начало массива JSON
ТекстФайл.Write("[");
Выборка = СоздатьОбъект("Справочник.Контрагенты");
Выборка.ВыбратьЭлементы();
ПерваяЗапись = 1;
Пока Выборка.ПолучитьЭлемент() = 1 Цикл
Если ПерваяЗапись = 0 Тогда
ТекстФайл.Write(","); // Разделитель объектов
КонецЕсли;
// Формируем JSON-строку для одного объекта
СтрЗаписи = "{""ID"":""" + СокрЛП(Выборка.Код) + """,""Name"":""" + СокрЛП(Выборка.Наименование) + """}";
ТекстФайл.Write(СтрЗаписи);
ПерваяЗапись = 0;
КонецЦикла;
// Записываем конец массива
ТекстФайл.Write("]");
ТекстФайл.Close();
Сообщить("Выгрузка завершена успешно!");
КонецПроцедуры
Стандартный JSON предполагает кодировку UTF-8. Объект FileSystemObject в 1С 7.7 умеет работать только с кодировками ANSI (Windows-1251) и UTF-16 (Unicode). Если принимающая сторона требует строго UTF-8, лучше использовать объект ADODB.Stream. Этот метод также является потоковым и не потребляет лишнюю оперативную память.
Рассмотрим алгоритм работы с ADODB.Stream:
Процедура СформироватьJSON_ADODB()
Стрим = СоздатьОбъект("ADODB.Stream");
Стрим.Type = 2; // Тип данных - текст
Стрим.Charset = "utf-8";
Стрим.Open();
Стрим.WriteText("[");
// Допустим, у нас есть ТаблицаЗначений ТЗ с данными
ТЗ.ВыбратьСтроки();
КолСтрок = ТЗ.КоличествоСтрок();
Для Ном = 1 По КолСтрок Цикл
ТЗ.ПолучитьСтрокуПоНомеру(Ном);
// Формируем JSON-строку
// Важно: здесь нужно экранировать спецсимволы, если они могут быть в данных
Значение1 = ЭкранироватьJSON(ТЗ.Поле1);
Значение2 = ЭкранироватьJSON(ТЗ.Поле2);
СтрОбъект = "{""f1"":""" + Значение1 + """,""f2"":""" + Значение2 + """}";
Если Ном < КолСтрок Тогда
СтрОбъект = СтрОбъект + ",";
КонецЕсли;
Стрим.WriteText(СтрОбъект);
КонецЦикла;
Стрим.WriteText("]");
// Сохраняем в файл
Стрим.SaveToFile("C:\export_utf8.json", 2);
Стрим.Close();
КонецПроцедуры
Если в вашей конфигурации используется мощная внешняя компонента 1С++, мы можем воспользоваться классом StringBuilder. Он специально оптимизирован для работы со сверхдлинными строками. В отличие от стандартных строк 1С, StringBuilder не перевыделяет память при каждом добавлении символа, а использует заранее зарезервированный буфер.
Этот метод работает значительно быстрее встроенных средств, но все же ограничен объемом доступной приложению памяти (около 2 Гб). Для 185 000 строк этого должно хватить, но для миллионов записей все равно лучше использовать потоковую запись в файл, как описано выше.
При формировании JSON вручную крайне важно корректно обрабатывать содержимое строковых полей. Если в наименовании товара встретится кавычка или обратный слеш, JSON-файл станет невалидным. Проанализируем, какие замены необходимо выполнить.
Создадим вспомогательную функцию для подготовки текста:
Функция ЭкранироватьJSON(Знач Стр)
Стр = СтрЗаменить(Стр, "\", "\\"); // Сначала заменяем слеши
Стр = СтрЗаменить(Стр, """", "\"""); // Затем кавычки
Стр = СтрЗаменить(Стр, Симв(10), "\n"); // Перевод строки
Стр = СтрЗаменить(Стр, Симв(13), "\r"); // Возврат каретки
Стр = СтрЗаменить(Стр, Симв(9), "\t"); // Табуляция
Возврат Стр;
КонецФункции
Важно: вызывайте эту функцию для каждого поля до того, как склеите их в общую структуру объекта. Это предотвратит ошибки при разборе файла на стороне получателя.
Если даже при использовании потоковой записи процесс идет медленно, можно применить метод записи «порциями» или «чанками». Вместо того чтобы вызывать метод WriteText для каждой строки (что создает нагрузку на дисковую подсистему), мы можем накапливать, например, по 1000 записей в обычной переменной 1С и записывать их одним махом.
Рассмотрим, как это реализовать:
Буфер = "";
Счетчик = 0;
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
Счетчик = Счетчик + 1;
Буфер = Буфер + СформироватьСтрокуJSON(ТЗ) + ",";
Если Счетчик % 1000 = 0 Тогда
// Записываем накопленную пачку в файл
Стрим.WriteText(Буфер);
Буфер = ""; // Очищаем память
КонецЕсли;
КонецЦикла;
// Не забываем дописать остаток буфера
Если ПустаяСтрока(Буфер) = 0 Тогда
Стрим.WriteText(Буфер);
КонецЕсли;
Подведем итоги нашего исследования проблемы нехватки памяти при формировании JSON в 1С 7.7:
+) для формирования больших объемов текста в памяти.Scripting.FileSystemObject (для простоты) или ADODB.Stream (для корректной кодировки UTF-8) для прямой записи в файл.StringBuilder из библиотеки 1С++ для ускорения строковых операций, если потоковая запись кажется слишком медленной.Применяя эти подходы, вы сможете успешно выгружать сотни тысяч и даже миллионы строк данных, сохраняя стабильность работы системы и не выходя за рамки доступной оперативной памяти.