Чтение таблиц DBF в 1С 8.3: рабочий код и разбор проблем

· 9 мин

Содержание

Прочитать DBF файл в 1С проще всего встроенным объектом XBase: открываете файл методом ОткрытьФайл(), задаёте кодировку и идёте по записям циклом Пока Не ВКонце(). Внешние компоненты не нужны, код работает и в файловой, и в клиент-серверной базе. Второй путь — COM: ADO-соединение через провайдер Visual FoxPro или Access Database Engine. Он нужен, когда XBase не понимает формат, например таблицы Visual FoxPro с типами Integer или DateTime.

На практике 80 строк кода закрывают почти все задачи: выписки банков, справочники ФИАС/КЛАДР старого образца, выгрузки из старых учётных программ. Основные грабли — кодировка, memo-файлы и разрядность провайдеров. Всё это ниже, с кодом.

Объект XBase: что умеет платформа без сторонних средств

XBase — объект встроенного языка для работы с файлами формата dBase. Он открывает, читает, создаёт и правит таблицы, умеет работать с индексами .cdx.

Главное, что нужно знать перед чтением:

  • 📄 Где доступен. На сервере, в толстом клиенте и во внешнем соединении. В тонком клиенте и веб-клиенте объекта нет, поэтому читать DBF из управляемой формы нужно в серверной процедуре.
  • 🔤 Кодировка. Свойство Кодировка принимает КодировкаXBase.OEM (DOS, cp866) или КодировкаXBase.ANSI (Windows-1251). По умолчанию стоит ANSI.
  • 📎 Memo-поля. Хранятся в отдельном файле .dbt рядом с таблицей. Если его нет или у него другое имя, файл не откроется.
  • 🗑 Удалённые записи. В DBF удаление — это пометка, запись физически остаётся до упаковки таблицы. Отсечь её помогает метод ЗаписьУдалена().

Ключевые методы для чтения:

Метод / свойствоЧто делает
ОткрытьФайл(Имя, Индекс, ТолькоЧтение)Открывает таблицу. Третий параметр Истина — только чтение
Открыта()Возвращает Истина, если файл открыт
ПоляКоллекция полей: у каждого есть Имя, Тип, Длина, Точность
Первая(), Следующая()Перемещение по записям
ВКонце()Истина, когда записи закончились
ПолучитьЗначениеПоля(ИмяИлиНомер)Значение поля текущей записи
КоличествоЗаписей()Число записей в файле
ЗакрытьФайл()Освобождает файл

К полю можно обратиться и как к свойству: DBF.NAME. Это удобно, когда структура файла известна заранее. Для универсальной загрузки лучше ПолучитьЗначениеПоля() — имя поля передаётся строкой.

Типы dBase переводятся в типы 1С так:

Тип в DBFКодЧто вернёт 1С
СимвольныйCСтрока (с хвостовыми пробелами)
ЧисловойN, FЧисло
ДатаDДата
ЛогическийLБулево
MemoMСтрока

Символьные поля в DBF дополняются пробелами до полной длины. Поле длиной 40 символов с текстом «ООО Ромашка» вернётся строкой из 40 символов. Всегда обрезайте значение через СокрП(), иначе поиск по наименованию в справочниках не найдёт совпадений.

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

Ниже универсальная функция: она читает любой DBF в таблицу значений, колонки создаёт по полям файла. Структуру заранее знать не нужно.

&НаСервереБезКонтекста
Функция ПрочитатьDBF(ИмяФайла, Кодировка)

    ТЗ = Новый ТаблицаЗначений;
    DBF = Новый XBase;
    DBF.Кодировка = Кодировка;
    DBF.ОткрытьФайл(ИмяФайла, , Истина);

    Если Не DBF.Открыта() Тогда
        ВызватьИсключение "Не удалось открыть файл " + ИмяФайла;
    КонецЕсли;

    Для Каждого Поле Из DBF.Поля Цикл
        ТЗ.Колонки.Добавить(Поле.Имя);
    КонецЦикла;

    DBF.Первая();
    Пока Не DBF.ВКонце() Цикл
        Если Не DBF.ЗаписьУдалена() Тогда
            НоваяСтрока = ТЗ.Добавить();
            Для Каждого Поле Из DBF.Поля Цикл
                Значение = DBF.ПолучитьЗначениеПоля(Поле.Имя);
                Если ТипЗнч(Значение) = Тип("Строка") Тогда
                    Значение = СокрП(Значение);
                КонецЕсли;
                НоваяСтрока[Поле.Имя] = Значение;
            КонецЦикла;
        КонецЕсли;
        DBF.Следующая();
    КонецЦикла;

    DBF.ЗакрытьФайл();
    Возврат ТЗ;

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

Передача файла с клиента на сервер

В клиент-серверной базе путь C:\Выписки\bank.dbf на компьютере пользователя сервер не видит. Файл сначала помещают во временное хранилище, а на сервере записывают во временный каталог:

&НаКлиенте
Процедура ЗагрузитьDBF(Команда)
    Оповещение = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
    НачатьПомещениеФайла(Оповещение, , , Истина, УникальныйИдентификатор);
КонецПроцедуры

&НаКлиенте
Процедура ПослеПомещенияФайла(Результат, Адрес, ВыбранноеИмяФайла, ДопПараметры) Экспорт
    Если Результат Тогда
        ЗагрузитьНаСервере(Адрес);
    КонецЕсли;
КонецПроцедуры

&НаСервере
Процедура ЗагрузитьНаСервере(Адрес)
    ДД = ПолучитьИзВременногоХранилища(Адрес);
    ИмяФайла = ПолучитьИмяВременногоФайла("dbf");
    ДД.Записать(ИмяФайла);

    ТЗ = ПрочитатьDBF(ИмяФайла, КодировкаXBase.OEM);
    УдалитьФайлы(ИмяФайла);

    Для Каждого Строка Из ТЗ Цикл
        // обработка строки: поиск контрагента, создание документа и т.п.
    КонецЦикла;
КонецПроцедуры

Таблица с memo-полями

У временного файла случайное имя, поэтому парный .dbt с ним не свяжется. Если в таблице есть memo-поля, поступайте так:

  1. На сервере создайте временный каталог: Каталог = ПолучитьИмяВременногоФайла(""); СоздатьКаталог(Каталог);.
  2. Запишите туда оба файла под одним базовым именем: data.dbf и data.dbt.
  3. Откройте data.dbf объектом XBase.
  4. После чтения удалите каталог целиком: УдалитьФайлы(Каталог);.

С клиента оба файла передаются одним вызовом множественного помещения или двумя вызовами НачатьПомещениеФайла.

Чтение DBF через COM: Visual FoxPro OLE DB и ADO

COM-путь нужен, когда файл создан в Visual FoxPro и использует типы, которых нет в классическом dBase: I (целое), Y (денежный), T (дата-время), B (двойная точность). Ещё один повод — SQL-запросы с отбором прямо к файлу.

Работает только под Windows. Варианты провайдеров:

ПровайдерСтрока подключенияРазрядность
Visual FoxPro OLE DBProvider=VFPOLEDB.1;Data Source=C:\Data\;только 32 бит
Access Database EngineProvider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Data\;Extended Properties=dBASE IV;32 и 64 бит
ODBC dBaseDriver={Microsoft dBASE Driver (*.dbf)};DriverID=277;Dbq=C:\Data\;32 бит

Data Source — это каталог, а не файл. Таблица указывается в тексте запроса.

Соединение = Новый COMОбъект("ADODB.Connection");
Соединение.Open("Provider=VFPOLEDB.1;Data Source=C:\Data\;");

Выборка = Новый COMОбъект("ADODB.Recordset");
Выборка.Open("SELECT * FROM bank.dbf WHERE summa > 0", Соединение);

Пока Не Выборка.EOF Цикл
    Счет  = СокрП(Выборка.Fields("SCHET").Value);
    Сумма = Выборка.Fields("SUMMA").Value;
    // обработка значений
    Выборка.MoveNext();
КонецЦикла;

Выборка.Close();
Соединение.Close();

Провайдер Visual FoxPro существует только в 32-битной версии. Если код выполняется на 64-битном сервере 1С или в 64-битном клиенте, ADO выдаст Провайдер не найден. Возможно, он установлен неправильно. (в английской Windows — Provider cannot be found. It may not be properly installed.). Выход: 32-битный клиент с чтением на клиенте, либо Access Database Engine x64.

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

Кодировка DBF: OEM, ANSI и нечитаемые символы

Кириллица в DBF хранится в одной из двух кодировок:

  • 🖥 cp866 (OEM, DOS) — файлы из старых DOS-программ, банковских систем, Клиент-Банков, ФИАС/КЛАДР.
  • 🪟 Windows-1251 (ANSI) — файлы из Windows-приложений, Excel, FoxPro для Windows.

Если в результате вместо «Иванов» видите €ў ­®ў или похожий набор символов, кодировка выбрана неправильно. Поменяйте КодировкаXBase.OEM на КодировкаXBase.ANSI или наоборот.

Как определить кодировку автоматически

В заголовке DBF по смещению 29 лежит байт кодовой страницы (Language Driver ID). Типичные значения для русских файлов: 0x65 (101) — cp866, 0xC9 (201) — Windows-1251. Многие программы оставляют там ноль, тогда остаётся проверка на тексте.

Надёжный приём: прочитайте первую запись в OEM и посчитайте долю русских букв в символьных полях. Если доля близка к нулю, а символов вроде Ђ, ў, ­ много — переоткройте в ANSI. Для банковских выписок кодировку проще один раз зафиксировать в настройках загрузки.

В ADO-провайдере Visual FoxPro кодировка берётся из того же байта заголовка. Если он пустой, а текст в файле в cp866, русские строки придут искажёнными. Тогда проще читать файл объектом XBase с явной кодировкой.

Если XBase не открывает файл: проверяем заголовок

Когда ОткрытьФайл() падает с ошибкой, стоит заглянуть в первые байты файла. Объект ЧтениеДанных (платформа 8.3.9 и выше) читает заголовок без сторонних утилит:

Чтение = Новый ЧтениеДанных(ИмяФайла);
Версия = Чтение.ПрочитатьБайт();          // байт 0: тип файла
Чтение.Пропустить(3);                      // байты 1-3: дата изменения
ЧислоЗаписей   = Чтение.ПрочитатьЦелое32(); // байты 4-7
ДлинаЗаголовка = Чтение.ПрочитатьЦелое16(); // байты 8-9
ДлинаЗаписи    = Чтение.ПрочитатьЦелое16(); // байты 10-11
Чтение.Пропустить(17);                     // байты 12-28
КодоваяСтраница = Чтение.ПрочитатьБайт();  // байт 29
Чтение.Закрыть();

Как расшифровать первый байт:

ЗначениеФорматЧто делать
0x03dBase III без memoЧитается XBase
0x83dBase III с memo .dbtXBase, рядом должен лежать .dbt
0x30Visual FoxProADO через VFPOLEDB или ACE
0xF5FoxPro с memo .fptADO через VFPOLEDB

Заодно сверьте размер: длина заголовка + число записей × длина записи должны примерно совпадать с размером файла (плюс 1 байт конца файла). Сильное расхождение — признак обрезанного при передаче файла.

Какой способ чтения DBF выбрать

КритерийXBaseADO / COM
УстановкаНичего не нужноПровайдер на каждом компьютере или сервере
ОС сервераWindows и LinuxТолько Windows
ФорматыdBase III/IV, memo .dbtПлюс Visual FoxPro, .fpt, новые типы полей
Отбор записейПеребором в циклеSQL WHERE прямо в запросе
КодировкаЗадаётся явноИз заголовка файла
Тонкий клиентТолько через серверРаботает на клиенте под Windows
Запись в файлДаДа, через INSERT/UPDATE

Короткое правило: начинайте с XBase. На COM переходите, только если файл не открывается или нужен SQL. Для базы на Linux-сервере COM недоступен вовсе.

Если не помогло ни то, ни другое

Откройте DBF в LibreOffice Calc: при открытии он спрашивает кодировку, можно перебрать варианты и увидеть читаемый текст. Затем сохраните как .xlsx и загрузите через ТабличныйДокумент.Прочитать() или типовую загрузку из файла. Для разовой задачи это быстрее, чем отлаживать провайдер.

Обработка ошибок при чтении DBF

Файл должен закрываться в любом случае, даже при сбое на середине. Иначе он остаётся занятым процессом 1С до перезапуска сеанса или рабочего процесса.

DBF = Новый XBase;
DBF.Кодировка = КодировкаXBase.OEM;
Попытка
    DBF.ОткрытьФайл(ИмяФайла, , Истина);
    // ... чтение записей ...
    DBF.ЗакрытьФайл();
Исключение
    Текст = ОписаниеОшибки();
    Если DBF.Открыта() Тогда
        DBF.ЗакрытьФайл();
    КонецЕсли;
    ЗаписьЖурналаРегистрации("Загрузка DBF", УровеньЖурналаРегистрации.Ошибка, , , Текст);
    ВызватьИсключение "Не удалось прочитать " + ИмяФайла + ": " + Текст;
КонецПопытки;

Ситуации, которые встречаются чаще всего:

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

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

Для больших файлов, от сотен тысяч записей, не собирайте всё в одну таблицу значений на сервере. Обрабатывайте записи прямо в цикле или пачками по 1 000–5 000 строк с записью объектов в отдельных транзакциях. Так вы не упрётесь в память рабочего процесса.

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

Можно ли прочитать DBF в 1С на сервере под Linux?
Да, объектом XBase. COM и ADO на Linux недоступны, поэтому файлы Visual FoxPro там придётся предварительно конвертировать.
Сколько символов может быть в имени поля DBF?
До 10 символов. Поэтому в выгрузках поля часто называются сокращённо: NAIMENOV, DATADOK, SUMMA_NDS.
Как узнать число записей до начала чтения?
Методом КоличествоЗаписей() после ОткрытьФайл(). Учтите: в это число входят и записи с пометкой удаления.
Можно ли читать DBF в тонком клиенте?
Объект XBase в тонком клиенте недоступен. Передайте файл на сервер через временное хранилище или читайте через COM на клиенте под Windows.
Почему в файле записей больше, чем видно в другой программе?
Скорее всего, часть записей помечена на удаление. Другие программы их скрывают, а при переборе XBase они попадаются, пока вы не проверите ЗаписьУдалена().