Как выгружать данные из 1С в текстовые файлы со специфическими и жесткими форматами

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции 1С с внешними информационными системами (особенно устаревшими банковскими хостами, государственными порталами или зарубежными ERP) разработчики часто сталкиваются с жесткими требованиями к форматам выгрузки. В техническом задании это обычно описывается как текстовый файл с фиксированной длиной строки (например, ровно 500 символов), где каждое поле должно занимать строго определенное количество позиций, иметь специфический формат дат (например, MM.DD.YYYY) и заполняться дефолтными значениями (пробелами или нулями), если данные отсутствуют.

В этой статье мы подробно разберем, как правильно реализовать подобную выгрузку на платформе 1С:Предприятие 8.3, задействовав штатные механизмы платформы, функции Библиотеки Стандартных Подсистем (БСП) и современные подходы к оптимизации производительности.

Понимание природы текстового формата

Первое и самое важное правило, которое нужно усвоить: в самом текстовом файле не существует никаких «типов данных». На принимающей стороне файл считывается как поток символов. Все требования к типам наподобие DATE(10) или NUMBER(12,2) — это правила валидации и разбора строки приемником. Задача разработчика 1С сводится к тому, чтобы преобразовать внутренние типы данных платформы в текстовое представление, жестко соответствующее спецификации.

Для решения этой задачи мы выделим несколько ключевых инструментов:

  1. Форматирование простых типов данных (даты, числа) с помощью встроенных функций платформы.
  2. Дополнение строк до фиксированной длины с использованием БСП или простых алгоритмов.
  3. Применение текстовых макетов для декларативного описания структуры файла.
  4. Оптимизация записи больших файлов с целью предотвращения утечек памяти и зависания системы.

Шаг 1. Форматирование дат и чисел

Для приведения дат и чисел к требуемому текстовому виду используется встроенная функция Формат(). Рассмотрим пример, когда дату необходимо выгрузить в формате MM.DD.YYYY, а при ее отсутствии заполнить поле 10 пробелами.


// Функция преобразования даты под требования формата
Функция ПолучитьДатуДляВыгрузки(ИсходнаяДата) Экспорт
    Если НЕ ЗначениеЗаполнено(ИсходнаяДата) Тогда
        // Возвращаем 10 пробелов по умолчанию
        Возврат "          "; 
    КонецЕсли;
    
    // Форматируем дату по маске "дд.мм.гггг" (MM.DD.YYYY)
    Возврат Формат(ИсходнаяДата, "ДФ=MM.dd.yyyy");
КонецФункции

Аналогично решается задача с числами. Если по спецификации число должно занимать ровно 12 символов, содержать 2 знака после запятой, не иметь разделителей групп и дополняться ведущими нулями слева:


Функция ПолучитьЧислоДляВыгрузки(ИсходноеЧисло) Экспорт
    // ЧГ=0 убирает разделители групп (пробелы между тысячами)
    // ЧЦ=12 задает общую длину числа
    // ЧДЦ=2 задает 2 знака после запятой
    ТекстЧисла = Формат(ИсходноеЧисло, "ЧЦ=12; ЧДЦ=2; ЧГ=0");
    
    // Дополняем нулями слева до 12 символов, если это требуется
    ТекстЧисла = СтроковыеФункцииКлиентСервер.ДополнитьСтроку(ТекстЧисла, 12, "0", НаправлениеДополнения.Лево);
    
    Возврат ТекстЧисла;
КонецФункции

Шаг 2. Выравнивание полей и фиксированная длина (БСП)

Для полей фиксированной длины (например, наименование контрагента должно занимать ровно 50 символов) нам необходимо либо обрезать слишком длинные строки, либо дополнять короткие пробелами справа. Писать для этого собственные циклы не нужно — в составе БСП есть готовый оптимизированный модуль СтроковыеФункцииКлиентСервер.

Рассмотрим применение метода ДополнитьСтроку() для подготовки строковых данных:


// Подготовка текстового поля фиксированной длины в 50 символов
ИсходныйТекст = "ООО Ромашка";
ЦелеваяДлина = 50;

// Сначала обрезаем строку, если она длиннее допустимого предела
Если СтрДлина(ИсходныйТекст) > ЦелеваяДлина Тогда
    ПодготовленныйТекст = Лев(ИсходныйТекст, ЦелеваяДлина);
Иначе
    // Дополняем пробелами справа до 50 символов
    ПодготовленныйТекст = СтроковыеФункцииКлиентСервер.ДополнитьСтроку(
        ИсходныйТекст, 
        ЦелеваяДлина, 
        " ", 
        НаправлениеДополнения.Право
    );
КонецЕсли;

Если в вашей конфигурации не используется БСП, эту функцию можно легко реализовать самостоятельно:


Функция ДополнитьСтрокуВручную(Знач ИсходнаяСтрока, Длина, СимволДополнения, Направление)
    Разность = Длина - СтрДлина(ИсходнаяСтрока);
    Если Разность <= 0 Тогда
        Возврат Лев(ИсходнаяСтрока, Длина);
    КонецЕсли;
    
    СтрокаДополнения = "";
    Для Сч = 1 По Разность Цикл
        СтрокаДополнения = СтрокаДополнения + СимволДополнения;
    КонецЦикла;
    
    Если Направление = "Лево" Тогда
        Возврат СтрокаДополнения + ИсходнаяСтрока;
    Иначе
        Возврат ИсходнаяСтрока + СтрокаДополнения;
    КонецЕсли;
КонецФункции

Шаг 3. Использование текстовых макетов для структурирования

Если формат выгрузки содержит множество колонок различной длины, жесткое кодирование позиций в программном коде быстро превратит его в нечитаемый хаос. Чтобы избежать этого, настроим структуру файла с помощью текстового макета.

Для этого в объекте метаданных (например, в нашей обработке выгрузки) добавим макет с типом Текстовый документ. В макете мы можем декларативно разметить строки с помощью параметров:


[СтрокаКонтрагента                                                               ]

В коде обработка макета будет выглядеть следующим образом:


Макет = ПолучитьМакет("МакетВыгрузки");
ОбластьСтроки = Макет.ПолучитьОбласть("СтрокаДанных");

ОбластьСтроки.Параметры.СтрокаКонтрагента = ПодготовленныйТекст;
ТекстДляЗаписи = ОбластьСтроки.ПолучитьТекст();

Шаг 4. Работа со списками допустимых значений

Часто в спецификациях требуется преобразовывать внутренние статусы 1С в жестко заданные коды приемника. Например, статус договора «Действует» должен выгружаться как код ACT, а статус «Закрыт» — как CLD. Для этого оптимально использовать объект Соответствие, инициализируемый один раз перед началом цикла выгрузки:


// Инициализируем карту соответствия статусов
ТаблицаСтатусов = Новый Соответствие;
ТаблицаСтатусов.Вставить(Перечисления.СтатусыДоговоров.Действует, "ACT");
ТаблицаСтатусов.Вставить(Перечисления.СтатусыДоговоров.Закрыт, "CLD");
ТаблицаСтатусов.Вставить(Перечисления.СтатусыДоговоров.Расторгнут, "CNC");

// В цикле получения данных
КодСтатуса = ТаблицаСтатусов[Выборка.СтатусДоговора];
Если КодСтатуса = Неопределено Тогда
    КодСтатуса = "   "; // Значение по умолчанию из 3 пробелов
КонецЕсли;

Этот подход работает значительно быстрее, чем постоянное использование тяжелых конструкций Если ... ИначеЕсли или обращение к базе данных.

Шаг 5. Оптимизация работы с большими объемами данных

Если вам предстоит ежедневно выгружать файлы объемом более 50 000 строк, неаккуратный код может привести к переполнению оперативной памяти и зависанию сервера приложений. Разберем основные правила оптимизации процесса выгрузки.

1. Откажитесь от объекта ТекстовыйДокумент при формировании файла

Объект ТекстовыйДокумент хранит все строки выгрузки непосредственно в оперативной памяти компьютера. При больших объемах это приводит к колоссальным затратам памяти. Вместо него используйте объект ЗаписьТекста, который записывает данные на диск порциями по мере их подготовки.


// Открываем файл для записи. Использование буфера происходит автоматически.
ЗаписьВыгрузки = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.UTF8);

Пока ВыборкаДанных.Следующий() Цикл
    СтрокаФайла = СформироватьСтрокуВыгрузки(ВыборкаДанных);
    ЗаписьВыгрузки.ЗаписатьСтроку(СтрокаФайла);
КонецЦикла;

ЗаписьВыгрузки.Закрыть();

2. Избавьтесь от обращений к реквизитам через точку в циклах

Каждое обращение вида Выборка.Контрагент.ИНН внутри цикла по 70 000 строкам порождает отдельный неявный запрос к базе данных. Все необходимые для выгрузки поля (даже данные связанных справочников) должны быть получены заранее в одном пакете запроса с помощью левых соединений (ЛЕВОЕ СОЕДИНЕНИЕ).

3. Решите проблему кодировки и маркера BOM (Byte Order Mark)

По умолчанию 1С записывает файлы в UTF-8 с добавлением специальной сигнатуры BOM в начало файла. Многие сторонние Unix-системы или парсеры на языках PHP/Python не умеют обрабатывать этот маркер корректно, из-за чего первая строка файла считывается с ошибками. Чтобы отключить запись BOM, передайте соответствующий параметр в конструктор объекта ЗаписьТекста:


// Ложь в шестом параметре отключает запись маркера BOM в начале файла
ЗаписьВыгрузки = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.UTF8, , , , Ложь);

Если принимающая сторона работает исключительно в кодировке DOS (например, для старых DBF или TXT шлюзов), используйте явное указание кодировки OEM:

ЗаписьВыгрузки = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.OEM);

4. Быстрая сборка строк через СтрШаблон() и СтрСоединить()

Сложение строк через оператор «+» в больших циклах работает медленно, так как платформа постоянно выделяет новые области памяти под растущие строки. Для сборки строки из отдельных полей используйте функцию СтрШаблон() (доступна начиная с платформы 8.3.6) или массив в связке с методом СтрСоединить():


// Пример быстрого формирования CSV-подобной строки с фиксированной длиной полей через СтрШаблон
ШаблонСтроки = "%1;%2;%3";
СтрокаДанных = СтрШаблон(ШаблонСтроки, 
    ПолучитьДатуДляВыгрузки(Выборка.Дата),
    ПолучитьЧислоДляВыгрузки(Выборка.Сумма),
    ПолучитьКодКонтрагента(Выборка.КонтрагентКод)
);

Комплексный пример оптимизированного алгоритма выгрузки

Объединим все описанные выше рекомендации в один готовый процедурный блок выгрузки данных, выполняемый на стороне сервера:


Процедура ВыгрузитьДанныеВТекст(ПутьКФайлу) Экспорт
    
    // 1. Подготавливаем запрос, выбирая все реквизиты сразу
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Продажи.Ссылка КАК Документ,
        |   Продажи.Дата КАК ДатаДокумента,
        |   Продажи.Контрагент.ИНН КАК ИННКонтрагента,
        |   Продажи.СуммаДокумента КАК Сумма
        |ИЗ
        |   Документ.РеализацияТоваровУслуг КАК Продажи
        |ГДЕ
        |   Продажи.Проведен";
        
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    
    // 2. Инициализируем объект последовательной записи без BOM в UTF-8
    ЗаписьФайла = Новый ЗаписьТекста(ПутьКФайлу, КодировкаТекста.UTF8, , , , Ложь);
    
    ШаблонВыгрузки = "%1|%2|%3";
    
    Пока Выборка.Следующий() Цикл
        
        // Форматируем дату под требования MM.DD.YYYY
        ТекстДата = Формат(Выборка.ДатаДокумента, "ДФ=MM.dd.yyyy");
        
        // Форматируем ИНН контрагента до строго фиксированной длины в 12 символов
        ТекстИНН = СтроковыеФункцииКлиентСервер.ДополнитьСтроку(
            СокрЛП(Выборка.ИННКонтрагента), 
            12, 
            " ", 
            НаправлениеДополнения.Право
        );
        
        // Форматируем сумму
        ТекстСумма = Формат(Выборка.Сумма, "ЧЦ=15; ЧДЦ=2; ЧГ=0");
        ТекстСумма = СтроковыеФункцииКлиентСервер.ДополнитьСтроку(
            ТекстСумма, 
            15, 
            "0", 
            НаправлениеДополнения.Лево
        );
        
        // Собираем строку и отправляем в буфер записи
        ИтоговаяСтрока = СтрШаблон(ШаблонВыгрузки, ТекстДата, ТекстИНН, ТекстСумма);
        ЗаписьФайла.ЗаписатьСтроку(ИтоговаяСтрока);
        
    КонецЦикла;
    
    // Закрываем файл и выгружаем остатки буфера на жесткий диск
    ЗаписьФайла.Закрыть();
    
КонецПроцедуры

Проанализировав данное решение, мы увидим, что оно работает стабильно при любых объемах данных, не нагружает оперативную память сервера, корректно обрабатывает пустые значения и полностью решает проблему интеграции с внешними системами, предъявляющими строгие требования к форматам обмена.

← На главную