Выгрузка данных из 1С в DBF: XBase, ADO и запись по байтам

· 11 мин

Содержание

Создать файл DBF в 1С проще всего встроенным объектом XBase: описываете поля через Поля.Добавить(), вызываете СоздатьФайл(), в цикле делаете Добавить() и Записать(), в конце ЗакрытьФайл(). Внешние драйверы не нужны, объект есть в платформе 8.3. Работает он на сервере и в толстом клиенте, поэтому код пишут в серверной процедуре, а готовый файл передают пользователю.

Если нужен формат, который XBase не даёт, остаются три пути: COM-объект ADODB.Connection и SQL-команды, внешняя обработка с универсальной выгрузкой и ручная сборка файла по байтам через ЗаписьДанных. Ниже каждый способ с рабочим кодом и подводными камнями.

Где сегодня нужен DBF и какой метод выбрать

DBF давно устарел, но его до сих пор требуют:

  • 🏦 банки — реестры на зачисление зарплаты и реестры платежей по старым форматам;
  • 🏢 управляющие компании и расчётные центры — выгрузки лицевых счетов и начислений;
  • 🧾 старые программы на FoxPro и Clipper, которые до сих пор работают у контрагентов;
  • 🗺 ГИС-системы: атрибуты карт в формате Shapefile хранятся именно в DBF.

Способы отличаются тем, что нужно поставить на компьютер и насколько точно вы управляете форматом.

МетодКогда подходитЧто нужноРиски
Объект XBaseпочти всегданичего, есть в платформетолько сервер и толстый клиент
COM ADODB + SQLнужен FoxPro-формат или SQL-логикапровайдер OLE DB на машине, где идёт кодразрядность 32/64, зависимость от Windows
Внешняя обработкавыгрузка повторяется, нужна формаправа на внешние обработкиобработку надо сопровождать
Запись по байтамприёмник капризен к заголовкуплатформа 8.3.9 и новееошибка в длине ломает весь файл
Какой у вас случай?

Создание DBF через объект XBase

Это основной способ. Объект XBase создаёт таблицу формата dBASE и пишет в неё записи. Поля описывают четырьмя параметрами: имя, тип, длина, точность.

Типы полей в XBase:

  • "S" — строка, длина до 254 символов;
  • "N" — число, длина считается вместе со знаком и десятичной точкой;
  • "D" — дата, хранится без времени;
  • "L" — булево;
  • "M" — длинный текст; рядом с DBF появляется отдельный memo-файл, и передавать нужно оба.

Имя поля — не длиннее 10 символов, латиницей. Кириллица в именах полей формально записывается, но большинство программ-приёмников такой файл не прочтёт.

Серверная часть: формируем файл

Пример выгружает справочник контрагентов из 1С:Бухгалтерии 3.0 в форму внешней обработки.

&НаСервере
Функция СоздатьDBFНаСервере()

    ИмяФайла = ПолучитьИмяВременногоФайла("dbf");

    ДБФ = Новый XBase;
    ДБФ.Кодировка = КодировкаXBase.OEM;
    ДБФ.Поля.Добавить("NPP", "N", 6, 0);
    ДБФ.Поля.Добавить("CODE", "S", 20);
    ДБФ.Поля.Добавить("NAME", "S", 100);
    ДБФ.Поля.Добавить("INN", "S", 12);
    ДБФ.Поля.Добавить("DELETED", "L");
    ДБФ.Поля.Добавить("EXPDATE", "D");
    ДБФ.СоздатьФайл(ИмяФайла);

    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |    Контрагенты.Код КАК Код,
    |    Контрагенты.Наименование КАК Наименование,
    |    Контрагенты.ИНН КАК ИНН,
    |    Контрагенты.ПометкаУдаления КАК ПометкаУдаления
    |ИЗ
    |    Справочник.Контрагенты КАК Контрагенты
    |ГДЕ
    |    НЕ Контрагенты.ЭтоГруппа";

    Выборка = Запрос.Выполнить().Выбрать();
    НомерСтроки = 0;
    Пока Выборка.Следующий() Цикл
        НомерСтроки = НомерСтроки + 1;
        ДБФ.Добавить();
        ДБФ.NPP     = НомерСтроки;
        ДБФ.CODE    = СокрЛП(Выборка.Код);
        ДБФ.NAME    = Лев(Выборка.Наименование, 100);
        ДБФ.INN     = Выборка.ИНН;
        ДБФ.DELETED = Выборка.ПометкаУдаления;
        ДБФ.EXPDATE = ТекущаяДатаСеанса();
        ДБФ.Записать();
    КонецЦикла;

    ДБФ.ЗакрытьФайл();

    Адрес = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ИмяФайла), УникальныйИдентификатор);
    УдалитьФайлы(ИмяФайла);
    Возврат Адрес;

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

ЗакрытьФайл() обязателен до чтения файла в ДвоичныеДанные. Пока файл открыт, в заголовке может стоять старое число записей, и приёмник увидит пустую таблицу.

Клиентская часть: отдаём файл пользователю

В клиент-серверном варианте файл создаётся во временном каталоге сервера 1С. Пользователь его не видит, поэтому файл передают через временное хранилище.

&НаКлиенте
Процедура ВыгрузитьВDBF(Команда)
    Адрес = СоздатьDBFНаСервере();
    ПолучаемыеФайлы = Новый Массив;
    ПолучаемыеФайлы.Добавить(Новый ОписаниеПередаваемогоФайла("kontragenty.dbf", Адрес));
    НачатьПолучениеФайлов(Новый ОписаниеОповещения("ПослеСохраненияФайла", ЭтотОбъект),
        ПолучаемыеФайлы, , Истина);
КонецПроцедуры

&НаКлиенте
Процедура ПослеСохраненияФайла(ПолученныеФайлы, ДополнительныеПараметры) Экспорт
    Если ПолученныеФайлы <> Неопределено Тогда
        ПоказатьОповещениеПользователя("Файл DBF сохранён");
    КонецЕсли;
КонецПроцедуры

Последний параметр Истина открывает диалог выбора папки. В файловой базе «сервер» — это компьютер пользователя, но схему с хранилищем лучше оставить: код не придётся переписывать при переходе на клиент-сервер.

Чтобы дописать строки в уже существующий файл, используйте ОткрытьФайл() вместо СоздатьФайл(), затем те же Добавить() и Записать().

COM-объект ADODB и SQL-запросы для записи DBF

Через COM можно работать с DBF как с базой данных: создать таблицу командой CREATE TABLE и залить строки INSERT. Способ нужен, когда приёмник ждёт таблицу Visual FoxPro или когда логику проще описать на SQL.

Пример на провайдере Microsoft ACE, он есть в 32- и 64-разрядном исполнении:

Соединение = Новый COMОбъект("ADODB.Connection");
Соединение.Open("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Export\;Extended Properties=dBASE IV;");

Соединение.Execute("CREATE TABLE payments (CODE CHAR(20), NAME CHAR(100), SUMMA DOUBLE, DOCDATE DATE)");

Наименование = СтрЗаменить("ООО ""Ромашка""", "'", "''");
Соединение.Execute("INSERT INTO payments (CODE, NAME, SUMMA, DOCDATE) VALUES ('0001', '"
    + Наименование + "', 1500.50, #10/07/2026#)");

Соединение.Close();

Что здесь важно:

  1. Data Source — это папка, а не файл. Имя таблицы становится именем файла: payments.dbf.
  2. Имя таблицы держите в пределах 8 символов: драйвер dBASE рассчитан на короткие имена файлов.
  3. Дата в литерале #...# пишется в американском порядке: месяц/день/год.
  4. Апострофы в строках удваивайте, иначе SQL-команда сломается на первом же «ООО О’Нил».

Для таблиц FoxPro используют провайдер VFPOLEDB.1 и синтаксис FoxPro:

CREATE TABLE payments FREE (CODE C(20), NAME C(100), SUMMA N(15,2), DOCDATE D)

Здесь длину и точность числа задаёте вы сами, как в XBase. Но провайдер VFP существует только в 32-разрядном варианте.

COM-объект создаётся там, где выполняется код. В клиент-серверной базе код на сервере ищет провайдер на сервере 1С, а он почти всегда 64-разрядный. Провайдер, установленный на компьютере бухгалтера, серверу не поможет.

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

Внешняя обработка и готовые библиотеки выгрузки

Когда выгрузка повторяется, код из примера выше переносят во внешнюю обработку (.epf). Конфигурацию трогать не нужно, обновления типовой базы не ломаются.

Порядок такой:

  1. В конфигураторе создайте внешнюю обработку, добавьте форму и команду Выгрузить.
  2. Вставьте серверную функцию и клиентскую процедуру из раздела про XBase.
  3. Вынесите на форму реквизиты: период, организацию, имя файла.
  4. Для разового запуска откройте обработку через главное меню: Файл → Открыть.
  5. Для постоянной работы подключите её в Администрирование → Печатные формы, отчеты и обработки → Дополнительные отчеты и обработки.

Для открытия внешних обработок у пользователя должно быть право на их интерактивное открытие. Без него пункт Файл → Открыть выдаст ошибку прав.

Практики делают в такой обработке универсальную функцию, которая принимает таблицу значений и сама описывает поля по типам колонок:

Тип колонки в 1СПоле DBFДлина и точность
Строка длиной NSN; неограниченную строку обрезают до 254
Число (разрядность P, дробная часть F)NP + 2 при F > 0, P + 1 при F = 0; точность F
ДатаD8
БулевоL1
СсылкаSнаименование или код строкой

Сведения о типе берут из Колонка.ТипЗначения: квалификаторы КвалификаторыСтроки.Длина и КвалификаторыЧисла.Разрядность. Имена полей задают отдельным соответствием «колонка → поле», потому что имена колонок обычно русские и длиннее 10 символов.

Готовые обработки «Выгрузка в DBF» легко найти в сети. Перед запуском откройте модуль и посмотрите код: внешняя обработка выполняется с вашими правами и может изменить данные базы.

Прямая запись байтов: собираем DBF вручную

Иногда приёмник отвергает файл из-за мелочи в заголовке: не та сигнатура, нулевой байт кодовой страницы. Тогда файл собирают сами через ЗаписьДанных (платформа 8.3.9 и новее).

Структура файла dBASE III:

БлокРазмерСодержимое
Заголовок32 байтасигнатура 0x03, дата правки (год − 1900, месяц, день), число записей, длина заголовка, длина записи, байт 29 — кодовая страница
Описание полей32 байта на полеимя (11 байт), тип, длина, точность
Конец заголовка1 байт0x0D
Записидлина записи × число записейфлаг удаления (пробел) + значения полей
Конец файла1 байт0x1A

Длина заголовка = 32 + 32 × число полей + 1. Длина записи = 1 + сумма длин полей. Для трёх полей длиной 20, 15 и 8 получится заголовок 129 байт и запись 44 байта.

&НаСервере
Процедура ЗаписатьDBFПобайтно(ИмяФайла, Таблица)
    // Таблица: колонки Код (Строка), Сумма (Число), Дата (Дата)
    Поля = Новый Массив;
    Поля.Добавить(Новый Структура("Имя, Тип, Длина, Точность, Колонка", "CODE", "C", 20, 0, "Код"));
    Поля.Добавить(Новый Структура("Имя, Тип, Длина, Точность, Колонка", "SUMMA", "N", 15, 2, "Сумма"));
    Поля.Добавить(Новый Структура("Имя, Тип, Длина, Точность, Колонка", "DOCDATE", "D", 8, 0, "Дата"));

    ДлинаЗаписи = 1;
    Для Каждого Поле Из Поля Цикл
        ДлинаЗаписи = ДлинаЗаписи + Поле.Длина;
    КонецЦикла;
    ДлинаЗаголовка = 32 + 32 * Поля.Количество() + 1;
    Сегодня = ТекущаяДатаСеанса();

    Запись = Новый ЗаписьДанных(ИмяФайла); // порядок байтов по умолчанию LittleEndian

    // Заголовок, 32 байта
    Запись.ЗаписатьБайт(3);
    Запись.ЗаписатьБайт(Год(Сегодня) - 1900);
    Запись.ЗаписатьБайт(Месяц(Сегодня));
    Запись.ЗаписатьБайт(День(Сегодня));
    Запись.ЗаписатьЦелое32(Таблица.Количество());
    Запись.ЗаписатьЦелое16(ДлинаЗаголовка);
    Запись.ЗаписатьЦелое16(ДлинаЗаписи);
    ЗаписатьНули(Запись, 17);
    Запись.ЗаписатьБайт(101); // 0x65 — кодовая страница 866
    ЗаписатьНули(Запись, 2);

    // Описания полей, по 32 байта
    Для Каждого Поле Из Поля Цикл
        Запись.ЗаписатьСимволы(Поле.Имя, "cp866");
        ЗаписатьНули(Запись, 11 - СтрДлина(Поле.Имя));
        Запись.ЗаписатьСимволы(Поле.Тип, "cp866");
        ЗаписатьНули(Запись, 4);
        Запись.ЗаписатьБайт(Поле.Длина);
        Запись.ЗаписатьБайт(Поле.Точность);
        ЗаписатьНули(Запись, 14);
    КонецЦикла;
    Запись.ЗаписатьБайт(13);

    // Записи
    Для Каждого Строка Из Таблица Цикл
        Запись.ЗаписатьСимволы(" ", "cp866");
        Для Каждого Поле Из Поля Цикл
            Значение = Строка[Поле.Колонка];
            Если Поле.Тип = "C" Тогда
                Текст = Лев(Значение, Поле.Длина);
                Текст = Текст + Пробелы(Поле.Длина - СтрДлина(Текст));
            ИначеЕсли Поле.Тип = "N" Тогда
                Текст = Формат(Значение, СтрШаблон("ЧДЦ=%1; ЧРД=.; ЧГ=0; ЧН=0", Поле.Точность));
                Если СтрДлина(Текст) > Поле.Длина Тогда
                    ВызватьИсключение "Число не помещается в поле " + Поле.Имя;
                КонецЕсли;
                Текст = Пробелы(Поле.Длина - СтрДлина(Текст)) + Текст;
            Иначе // D
                Текст = ?(ЗначениеЗаполнено(Значение), Формат(Значение, "ДФ=yyyyMMdd"), Пробелы(8));
            КонецЕсли;
            Запись.ЗаписатьСимволы(Текст, "cp866");
        КонецЦикла;
    КонецЦикла;

    Запись.ЗаписатьБайт(26);
    Запись.Закрыть();
КонецПроцедуры

&НаСервере
Процедура ЗаписатьНули(Запись, Количество)
    Для Сч = 1 По Количество Цикл
        Запись.ЗаписатьБайт(0);
    КонецЦикла;
КонецПроцедуры

&НаСервере
Функция Пробелы(Количество)
    Результат = "";
    Для Сч = 1 По Количество Цикл
        Результат = Результат + " ";
    КонецЦикла;
    Возврат Результат;
КонецФункции

Обратите внимание на тип строки: в самом файле строковое поле обозначается буквой C, а S — это обозначение только для объекта XBase.

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

Кодировка и кириллица в DBF

Кракозябры — самая частая жалоба на выгрузку. Причина почти всегда в несовпадении кодовых страниц.

ВариантКодовая страницаБайт 29Кто обычно ждёт
КодировкаXBase.OEM866 (DOS)0x65 или 0x26FoxPro, Clipper, большинство банковских форматов
КодировкаXBase.ANSI1251 (Windows)0xC9программы под Windows, ГИС, Excel

Как действовать:

  1. Спросите у получателя кодировку прямо. Формат реестра обычно её указывает.
  2. Задайте ДБФ.Кодировка до вызова СоздатьФайл().
  3. Если приёмник читает кодовую страницу из байта 29 и показывает мусор, откройте файл в HEX-редакторе и сверьте этот байт с таблицей.

Символов «ёлочек», длинного тире и знака евро в 866-й странице нет. Перед записью замените их: СтрЗаменить(Текст, "«", """"). Буква «ё» и знак «№» в 866-й есть.

Ещё одна ловушка — длина. В DBF длина строкового поля считается в байтах, а 866 и 1251 однобайтовые, поэтому 100 символов = 100 байт. Длинное наименование обрезайте через Лев() сами, чтобы видеть, где именно срезано.

Автоматическая выгрузка в DBF по расписанию

Есть два рабочих варианта без правки типовой конфигурации.

Регламентное задание в расширении. В расширении создайте общий модуль с процедурой выгрузки и регламентное задание, которое её вызывает. Файл пишется в сетевую папку, доступную службе сервера 1С. В файловой базе регламентные задания выполняются только при открытом сеансе, поэтому для неё надёжнее второй вариант.

Планировщик Windows и параметр /Execute. Внешняя обработка открывается сразу после запуска 1С, выгружает файл и закрывает программу:

"C:\Program Files\1cv8\common\1cestart.exe" ENTERPRISE /F"D:\Bases\Buh" /N"Выгрузка" /P"пароль" /Execute"D:\Scripts\export_dbf.epf"

В модуле формы обработки вызовите серверную выгрузку в ПриОткрытии, затем ЗавершитьРаботуСистемы(Ложь). Для такого запуска заведите отдельного пользователя с минимальными правами: пароль лежит в задании планировщика открытым текстом.

Добавляйте в имя файла дату: reestr_20261007.dbf. Так вчерашний реестр не перезапишется сегодняшним, если получатель ещё не забрал файл.

Ошибки при создании DBF и как их распознать

Диагностика: выберите симптом

Частые вопросы

Можно ли создать DBF в веб-клиенте 1С?
Да, если формирование файла идёт на сервере. В веб-клиенте объект XBase недоступен, но серверная процедура создаёт файл и передаёт его в браузер через временное хранилище.
Как удалить записи из готового DBF средствами 1С?
Откройте файл методом ОткрытьФайл(), встаньте на запись и вызовите Удалить(). Запись только помечается; физически её убирает метод Сжать().
Чем проверить созданный DBF без программы-приёмника?
Подойдёт любой просмотрщик DBF или LibreOffice Calc: при открытии он спрашивает кодировку. Современный Excel открывает DBF, но сохранять в этот формат не умеет.
Сколько строк можно записать в один DBF?
Число записей в заголовке хранится в четырёх байтах, так что ограничивает обычно не формат, а программа-приёмник и размер файла. Для больших выгрузок уточните лимит у получателя.
Можно ли прочитать DBF обратно в 1С?
Да, тем же объектом XBase: ОткрытьФайл(), затем Первая() и цикл со Следующая() до ВКонце(), значения полей читаются по их именам.