Структура DBF: справочник по формату
Формату больше сорока лет, спецификация разошлась по диалектам, а единого стандарта нет. Здесь собрано то, что нужно знать, чтобы прочитать файл или понять, почему его не читает чужая программа.
Заголовок: первые 32 байта
| Смещение | Размер | Что там |
|---|---|---|
| 0 | 1 | версия формата: 0x03 dBase III, 0x30 Visual FoxPro, 0x83 dBase III с memo |
| 1–3 | 3 | дата последнего изменения: год (две цифры), месяц, день |
| 4–7 | 4 | число записей |
| 8–9 | 2 | длина заголовка — отсюда начинаются данные |
| 10–11 | 2 | длина одной записи |
| 28 | 1 | флаги таблицы: 0x01 есть .cdx, 0x02 есть memo, 0x04 принадлежит .dbc |
| 29 | 1 | код языка (кодировка) |
Полезное следствие: число записей можно проверить арифметикой. (размер файла − длина заголовка) ÷ длина записи должно совпадать со счётчиком. Не совпадает — файл обрезан или счётчик врёт.
Дескрипторы полей
Сразу за заголовком идут описания полей по 32 байта каждое, а за ними — байт 0x0D как признак конца списка.
В каждом дескрипторе: имя (11 байт, дополнено нулями), тип (1 байт), смещение поля (4 байта), длина (1 байт), число знаков после запятой (1 байт), флаги (1 байт).
Отдельный случай — dBase Level 7: там имя занимает 32 байта, а сам дескриптор 48. Отличить можно структурно: проверить, при какой раскладке сумма длин полей сходится с длиной записи из заголовка.
Типы полей
| Тип | Что это | Как хранится |
|---|---|---|
C | строка | текст, дополненный пробелами справа |
N, F | число | текстом, с выравниванием вправо |
D | дата | восемь символов ГГГГММДД |
L | логическое | один символ: T, F, Y, N или пробел |
M | memo | номер блока во внешнем файле |
I | целое | 4 байта, little-endian |
T | дата и время | 8 байт: джулианский день и миллисекунды |
Y | денежный | 8 байт целого, делится на 10 000 |
B | double в Visual FoxPro, memo в dBase | зависит от версии — частый источник ошибок |
Числа текстом — не архаизм, а особенность: N(12,2) хранит ровно то, что видно, без двоичного округления. Поэтому суммы в DBF не «плывут» так, как в форматах с плавающей точкой.
Записи и признак удаления
Записи идут подряд, все одинаковой длины — адрес считается умножением, поэтому доступ к любой записи мгновенный.
Первый байт каждой записи служебный: пробел — запись активна, звёздочка — помечена на удаление. Подробно про это в отдельной статье.
В Visual FoxPro добавлено системное поле _NullFlags: битовая маска, где каждый бит говорит, что соответствующее поле содержит NULL. Без её разбора пустые значения выглядят как нули и пустые строки — разница существенная.
Частые вопросы
Почему один и тот же тип читается по-разному?
Из-за диалектов. Классический пример — тип B: в Visual FoxPro это число двойной точности, в dBase — ссылка на двоичное memo.
Как узнать версию формата?
По первому байту файла. Но версия говорит только о диалекте: наличие memo надёжнее определять по фактическим типам полей.
Зачем нужен .cdx рядом с файлом?
Это индекс для быстрого поиска и сортировки. Данные в нём дублируются, поэтому после правки таблицы в обход индекса его нужно перестроить.