При интеграции 1С:Предприятие с внешними системами мы часто сталкиваемся с необходимостью обмена данными через файлы формата DBF (dBase, FoxPro). Казалось бы, работа с этим форматом давно стандартизирована через встроенный объект платформы XBase. Однако на практике возникают нюансы, способные нарушить логику обработки данных.
Рассмотрим ситуацию, с которой часто сталкиваются разработчики: нам поступает входящий DBF-файл, в котором есть числовые колонки. В некоторых строках в этих колонках стоит явное значение 0, а в других — значение отсутствует (поле пустое, NULL, BLANK). При попытке прочитать этот файл стандартными средствами 1С мы обнаруживаем, что платформа приводит оба варианта к одному знаменателю — к нулю.
Это становится критической проблемой, если наша задача — не просто прочитать данные, но и, например, загрузить их во временное хранилище, обработать и сохранить обратно в DBF, сохранив исходную структуру. Если мы не различаем "пусто" и "ноль", то при обратной записи мы затрем пустые поля нулями, что может нарушить работу стороннего программного обеспечения, ожидающего именно пустоту (например, как признак отсутствия данных или необработанной записи).
В этой статье мы подробно разберем, почему так происходит, и проанализируем способы решения этой задачи, выходящие за рамки стандартного функционала XBase.
Чтобы понять суть проблемы, нам нужно заглянуть "под капот" формата DBF. Файлы этого типа хранят данные в текстовом виде (для большинства типов полей). Для числового поля (Type 'N') разница на уровне байтов выглядит так:
Когда мы используем код вида:
ФайлDBF = Новый XBase;
ФайлDBF.ОткрытьФайл(ПутьКФайлуDBF,,Истина);
Пока ФайлDBF.Следующая() Цикл
Значение = ФайлDBF.ПолеЧисло; // Здесь всегда будет число
КонецЦикла;
Платформа 1С, встречая строку из пробелов в числовом поле, автоматически парсит её и преобразует в значение по умолчанию для типа Число — то есть в 0. У объекта XBase нет настроек, позволяющих отключить это поведение или получить "сырое" значение поля. Это сделано для удобства и типизации, но в нашем случае играет против нас.
Самым надежным и профессиональным способом решения данной задачи является отказ от использования объекта XBase в пользу COM-объекта ADODB.Connection. Технология ADO позволяет работать с DBF-файлами как с базой данных, используя SQL-запросы. Драйверы баз данных (провайдеры) умеют корректно обрабатывать состояние NULL.
Давайте рассмотрим алгоритм чтения, который позволяет отличить ноль от пустоты.
Для работы нам понадобится провайдер. Чаще всего используется Microsoft.Jet.OLEDB.4.0 (для старых форматов dBase) или VFPOLEDB (Visual FoxPro OLE DB Provider). Второй вариант предпочтительнее для сложных файлов.
// Создаем соединение
Соединение = Новый COMОбъект("ADODB.Connection");
// Формируем строку подключения. Указываем путь к ПАПКЕ, где лежит файл
КаталогФайла = "C:\ExchangeData\";
СтрокаПодключения = "Provider=VFPOLEDB;Data Source=" + КаталогФайла + ";Mode=ReadWrite|Share Deny None;Collating Sequence=Machine;";
Попытка
Соединение.Open(СтрокаПодключения);
Исключение
Сообщить("Ошибка подключения: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
Теперь мы можем выполнить SQL-запрос к нашему файлу (имя файла выступает в роли имени таблицы) и проверить значение на NULL.
ИмяФайла = "mydata.dbf"; // Только имя, без пути
НаборЗаписей = Новый COMОбъект("ADODB.Recordset");
// Выбираем все поля
ЗапросSQL = "SELECT * FROM " + ИмяФайла;
НаборЗаписей.Open(ЗапросSQL, Соединение);
Пока НЕ НаборЗаписей.EOF Цикл
// Читаем наше проблемное поле
ПолеЗначение = НаборЗаписей.Fields("CountField").Value;
// Проверяем тип значения
Если ПолеЗначение = Null Тогда
// ЗДЕСЬ ПУСТО!
// Логика обработки пустого значения
Сообщить("В строке пустое значение");
Иначе
// Здесь число (в том числе может быть 0)
ЧисловоеЗначение = Число(ПолеЗначение);
Сообщить("В строке число: " + ЧисловоеЗначение);
КонецЕсли;
НаборЗаписей.MoveNext();
КонецЦикла;
НаборЗаписей.Close();
Соединение.Close();
Важно: При использовании ADO значение Null в 1С имеет специальный тип Null. Его нельзя напрямую складывать с числами, поэтому проверка Если ПолеЗначение = Null является обязательной.
Если вам нужно сохранить данные обратно в DBF с сохранением пустот, использование XBase также не подойдет, так как присваивание ФайлDBF.Поле = Неопределено или Null обычно приводит к ошибке или записи нуля. Через ADO это делается SQL-командой UPDATE.
// Пример обновления поля в пустое значение
Команда = Новый COMОбъект("ADODB.Command");
Команда.ActiveConnection = Соединение;
// Устанавливаем NULL для поля CountField где ID = 100
ТекстКоманды = "UPDATE " + ИмяФайла + " SET CountField = NULL WHERE ID = 100";
Команда.CommandText = ТекстКоманды;
Команда.Execute();
Если использование COM-объектов невозможно (например, вы работаете на сервере под управлением Linux, где нет драйверов Microsoft OLE DB), нам придется прибегнуть к организационным мерам.
Как справедливо заметили участники обсуждения, проблема часто кроется в методологии. Если мы сами формируем этот файл или можем договориться с поставщиком данных, следует использовать "Сентинел" (Sentinel Value) — специальное значение, которое будет обозначать "Пусто".
Обычно в качестве такого значения выбирают число, которое никогда не может встретиться в реальных данных. Например: -999999.
Алгоритм действий:
Null или Неопределено, записываем в XBase число -999999.Если Значение = -999999 Тогда считаем, что это пустая ячейка. Иначе — это реальное число.Этот метод работает стабильно со штатным XBase, но требует контроля над обеими сторонами обмена (и отправителем, и получателем).
Существует еще один хитрый способ работы с XBase, если структура файла нам заранее известна, но менять её мы не можем. Мы можем попытаться "обмануть" платформу, описав структуру полей вручную перед открытием файла, задав числовому полю строковый тип.
Однако, метод XBase.ОткрытьФайл считывает заголовок реального файла и использует типы данных, прописанные там. Поэтому просто переопределить тип не получится, если файл уже существует.
Но если мы создаем промежуточную таблицу значений, мы можем использовать следующий подход для анализа (но не модификации) файла:
ЧтениеТекста).Давайте подведем итог. Проблема интерпретации пустых числовых полей как нуля в DBF вызвана стандартным поведением механизма XBase в платформе 1С. Это не ошибка, а особенность реализации.
Для решения задачи у нас есть два основных пути:
ADODB.Connection для выполнения SQL-запросов к DBF-файлу. Это позволяет корректно читать NULL и записывать его обратно. Это наиболее профессиональный подход.Если вы работаете в операционной системе Windows, мы настоятельно рекомендуем использовать первый вариант через ADO, так как он обеспечивает максимальную точность данных.