В данной серии статей опишу создание 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.
Привет
ссылка не работает
поделись классиком пожалуйста очень нужно
spam7379@mail.ru