В данной серии статей опишу создание ExtJS приложения для просмотра DBF файлов. DBF — это формат баз данных используемых в Microsoft FoxPro, некогда популярное средство разработки десктопных приложений.

 Работающий пример онлайн редактора DBFможно посмотреть по этой ссылке: http://jobtools.ru/js/dbfshow/ 

Логично предположить, что реализация делится на серверную — это PHP и клиентскую часть — javascript с использованием ExtJS. Начнем с серверной части.

Для начала необходимо разобраться с форматом файлов DBF, что он из себя представляет:

Структура файла dbf

32

Заголовок файла DBF

n*m

Дескрипторы полей в количестве n. m=32

1

Терминальный байт CHR(13).

m*k

Записи с данными (длиной k и количеством m)

Заголовок файла dbf

0

0x00

1

Сигнатура.

1

0x01

3

Дата последней модификации в виде ГГММДД

4

0x04

4

Число записей в базе

8

0x08

2

Полная длина заголовка (с дескрипторами полей)

10

0x0A

2

Длина одной записи

12

0x0C

2

Зарезервировано (всегда 0)

14

0x0E

1

Флаг, указывающий на наличие незавершенной транзакции dBASE IV

15

0x0F

1

Флаг шифрования таблицы dBASE IV

16

0x10

12

Зарезервированная область для многопользовательского использования

28

0x1C

1

Флаг наличия индексного MDX-файла

29

0x1D

1

Идентификатор кодовой страницы файла (dBASE IV, Visual FoxPro, XBase).

30

0x1E

2

Зарезервировано (всегда 0)

32

0x20

32

Только в dBASE 7. Идентификатор языкового драйвера.

64

0x40

4

Только в dBASE 7. Зарезервировано

Описание поля (колонки)

0

0x00

11

Имя поля

11

0x0B

1

Тип поля

12

0x0C

4

Зарезервировано

16

0x10

1

Полная длина поля

17

0x11

1

Число десятичных разрядов; для типа C — второй байт длины поля

30

0x1E

13

Зарезервировано (всегда 0)

31

0x1F

1

Флаг тэга файла MDX (только в dBASE IV)

Приступим к созданию класса DBF:

<?php

class dbf {
    static $filename = '075.dbf';   // Имя файла
    static $file;                   //дескриптор файла
    static $header;                 //заголовок
    static $countColumns;           //Кол-во колонок
    static $countRows;              //Кол-во строк
    static $columns  = array();     //Массив колонок</pre>
?>

 

Чтение dbf начинаем с чтения его заголовка. Смотрим на его структуру и пишем код чтения первых 32 байтов. Оформим это в отдельную процедуру и назовем её ReadHeaders:

    function readHeader()
    {
        self::$file = fopen(self::$filename, 'r');          //Открыли файл
        $tmp = fread(self::$file,12);                          //Считываем первые 12 байтов - в них содержится всё что нам нужно, остальное просто не учитываем
        $format = 'C1BYTE1/C1YY/C1MM/C1DD/l1RECORDSCOUNT/s1HEADERSIZE/s1RECORDSIZE';           //Описываем формат этих 12 байт
        self::$header = unpack($format, $tmp);                                   //Производим распаковку наших 12 байт считанных в $tmp, по формату $format
        self::$countColumns = (self::$header['HEADERSIZE'] - 33) / 32;          //Высчитываем кол-во колонок длина заголовка (вместе с колонками) - размер заголовка/размер описания 1-ой колонки
        self::$countRows = (self::$header['RECORDSCOUNT']);                    //кол-во строк
    }

Чтение данных о колонках оформим процедурой ReadColumns()

function readColumns()
    {
        fseek(self::$file, 32);         //переместились на 33 байт. С него начинается описание колонок
        $formatColumn = 'A11NAME/c1TIP/l1RESERVED1/C1SIZEBIN/C1ZPT/s1RESERVED2/C1ID/l1RESERVED3/s1RESERVED4/C1MDX/l1POS';     // описали формат колонок
        $pos =0;
        $posColumn = 0;
        $cnt = 0;
        for ($i=1;$i<=self::$countColumns;$i++)
        {                        
            $tmp = fread(self::$file,32);           //считываем данные о колонке
            $column = unpack($formatColumn, $tmp);  //распаковываем данные о колонке
            $posColumn = $pos;                      
            $pos = $pos + $column['SIZEBIN'];       //Прибавляем размер еткущей колонки                 
            if ($column['TIP']!=0)                  // Если тип колонки не <>0 только для предохранения 8)
            {
                 array_push(self::$columns, array(  //Добавляем в массив колонок данные о текущей колнке
                    'NAME'=>trim($column['NAME']), 
                    'TIP'=>chr($column['TIP']),
                    'ZPT'=>$column['ZPT'], 
                    'SIZEBIN'=>$column['SIZEBIN'],
                    'MDX'=>$column['MDX'],
                    'POS'=>$posColumn                
                ));
                $cnt++;
            }
        }
        self::$countColumns = $cnt;     //Общее кол-во колонок
}

Процедура для чтения значения ячейки в строке $row, столбце $column:

    function getValue($row, $column)
    {
        if ($row>self::$header['RECORDSCOUNT']) return "ERR";    //проверка на существование строки
        if ($column> self::$countColumns) return "ERR";    //проверка на диапазон кол-ва столбцов
        fseek(self::$file, self::$header["HEADERSIZE"]+1+$row*self::$header["RECORDSIZE"]+self::$columns[$column]["POS"]);     //Считываем бинарные данные указанной ячейки
        $buf = fread(self::$file,self::$columns[$column]["SIZEBIN"]);
        return $this->parseValue(self::$columns[$column]['TIP'], $buf);         //Вызываем спец процедуру по форматированию считанного значения в зависимости от типа колонки
    }

 

    function parseValue($tip, $buffer)
    {
        if (($tip != 'D')&&($tip != 'T'))            //Если тип поля не D- дата и нет T- Datetime
            return iconv("WINDOWS-1251", "WINDOWS-1251",$buffer);    
        if ($tip == 'T')
        {
            return $this->JulianDate($buffer);            
//return jdtogregorian((float)$buffer);
        }
        if ($tip == 'D')
        {
            $tmp .= $buffer[6];
            $tmp .= $buffer[7];
            $tmp .= '.';
            $tmp .= $buffer[4];
            $tmp .= $buffer[5];
            $tmp .= '.';
            $tmp .= $buffer[0];
            $tmp .= $buffer[1];
            $tmp .= $buffer[2];
            $tmp .= $buffer[3];            
            return $tmp;
        }
    }

Остальные процедуры являются вспомогательными, такие как: getColumnType, getColumnSize и т.п., основной костяк мы описали. Полный исходный код файла dbf.php можно взять здесь dbf.php.

В следующей статье начнем разрабатывать клиентскую часть на js.

Просмотр DBF файлов онлайн на ExtJS. Часть 1. Формат DBF
Метки:        

Одно мнение о “Просмотр DBF файлов онлайн на ExtJS. Часть 1. Формат DBF

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Нажимая на кнопку "Отправить комментарий", я даю согласие на обработку персональных данных и соглашаюсь c политикой конфиденциальности *