Возникла вот необходимость работы с базами КЛАДР, которые хранятся в dBase - *.dbf. Поддержку dBase из пхп уже убрали, пакет dbf2mysql из репозитория транслировал в мускуль только маленькие файлы (из КЛАДР в мускуль ушли только файлы размером меньше 1 мб, 8 и более мб файлы почему-то не записались в мускуль этим пакетом), найденный одноимённый класс dbf_class почему-то неправильно читал файлы... ну в общем решил написать свой, чтобы был
<?
/*
DBF reader class by Ramil Minnigaliev (aka Thunder)
version 0.1
13 aug 2010
*/
class dbf_class {
//Private Properties
private $raw, $version, $modify_date, $recs_num, $full_header_len, $rec_len, $fields_num, $header_size = 32, $f,
$header, $recs_count;
//Class Constructor
function dbf_class($file_name) {
//Check DBF File
if ((!file_exists($file_name))or(strcasecmp(substr($file_name,-4), '.dbf')!=0)) {
echo 'Not a valid DBF file!'; exit;
}
//Read the File
$this->f = fopen($file_name, "rb");
if (!$this->f) { echo "Cannot read DBF file"; exit; }
$file_size = filesize($file_name);
$this->raw = fread($this->f, $this->header_size);
//Check DBF Version
if (!((ord($this->raw[0]) == 3) or (ord($this->raw[0]) == 131) or (ord($this->raw[0]) == 245) or (ord($this->raw[0]) == 139))) {
echo 'Not a valid DBF file!'; exit;
}
$arrHeaderHex = array();
for($i = 0; $i < $this->header_size; $i++){
$arrHeaderHex[$i] = str_pad(dechex(ord($this->raw[$i])), 2, "0", STR_PAD_LEFT);
}
//Version of DBF
$this->version = hexdec($arrHeaderHex[0]);
//Date of Last Modify
$this->modify_date = hexdec($arrHeaderHex[3]).'.'.hexdec($arrHeaderHex[2]).'.'.hexdec($arrHeaderHex[1]);
//Number of Records
$this->recs_num = hexdec($arrHeaderHex[7].$arrHeaderHex[6].$arrHeaderHex[5].$arrHeaderHex[4]);
//Full Header's Length (Header Size + Field Descriptor)
$this->full_header_len = hexdec($arrHeaderHex[9].$arrHeaderHex[8]);
//Record's Length
$this->rec_len = hexdec($arrHeaderHex[11].$arrHeaderHex[10]);
//Number of Fields
$this->fields_num=(($this->full_header_len-1)/$this->header_size)-1;
//Record's Count
$this->recs_count = $this->recs_num;
//Field's Description
$i=0;
while ($i++ < $this->fields_num){
$this->header[$i]['name'] = $this->zero_code_cut(fread($this->f,11));
$this->header[$i]['type'] = $this->zero_code_cut(fread($this->f,1));
$this->header[$i]['mem_addr'] = dechex(ord(fread($this->f,1))).dechex(ord(fread($this->f,1))).dechex(ord(fread($this->f,1))).dechex(ord(fread($this->f,1)));
$this->header[$i]['field_len'] = ord(fread($this->f,1));
$this->header[$i]['deciminal_len'] = ord(fread($this->f,1));
fseek($this->f,14,SEEK_CUR);
}
//Shift one byte (Mark of delete)
fseek($this->f,1,SEEK_CUR);
}
function version() {return $this->version;}
function modify_date() {return $this->modify_date;}
function recs_num() {return $this->recs_num;}
function full_header_len() {return $this->full_header_len;}
function rec_len() {return $this->rec_len;}
function fields_num() {return $this->fields_num;}
function header() {return $this->header;}
//Delete Empty
function zero_code_cut($str_i){
while (ord($str_i[++$i]) != 0) {}
return substr($str_i, 0, $i);
}
//Get Next Record
function next_rec() {
if (!$this->check_next_rec()) return 0;
$this->recs_count--;
fseek($this->f,1,SEEK_CUR);
while ($i++ < $this->fields_num)
$rec[$i] = iconv('CP866','UTF-8',fread($this->f,$this->header[$i]['field_len']));
return $rec;
}
//Check Next Record
function check_next_rec() {
if ($this->recs_count <= 0) {fclose($this->f);return 0;}
return 1;
}
//Set Next Record
function set_next_rec($rec_num) {
if ((feof($this->f))or!($rec_num>0)) return 0;
$this->recs_count = $this->recs_num - $rec_num;
fseek($this->f,$rec_num * $this->rec_len,SEEK_CUR);
return 1;
}
}
?>
<html lang="ru">
<head>
<title>DBF2MYSQL</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel='stylesheet' type='text/css' href='./style.css'>
</head>
<body>
<div class='iblock'>
<?
if (isset($_GET['dir']) and isset($_GET['iblock'])){
require "./dbf_class.php";
require "./config.php";
$dir = $_GET[dir];
$iblock = $_GET[iblock];
$thefile = $iblock.".DBF";
if (isset($_GET[rec_num])) $rec_num = $_GET[rec_num];
else $rec_num = 0;
$dbf = new dbf_class($dir.$thefile);
$recs_num = $dbf->recs_num();
if ($recs_num > 0) {
$progress = round($rec_num*100/$recs_num);
$dbf->set_next_rec($rec_num);
}
else $progress = 100;
echo "<p>Инфоблок: $iblock</p>";
echo "<p>Импортированных записей: $rec_num</p>";
echo "<p>Прогресс: $progress %</p>";
if (($_GET['go']>0)and($recs_num>$rec_num)){
if ($rec_num==0) switch ($iblock) {
case "ALTNAMES":
$sql = mysql_query("TRUNCATE TABLE $tb_altnames;");
break;
case "DOMA":
$sql = mysql_query("TRUNCATE TABLE $tb_doma;");
break;
case "FLAT":
$sql = mysql_query("TRUNCATE TABLE $tb_flat;");
break;
case "KLADR":
$sql = mysql_query("TRUNCATE TABLE $tb_kladr;");
break;
case "SOCRBASE":
$sql = mysql_query("TRUNCATE TABLE $tb_socrbase;");
break;
case "STREET":
$sql = mysql_query("TRUNCATE TABLE $tb_street;");
break;
}
$i = 0;
$go = $_GET['go'];
while ($i++<$go) {
if ($dbf->check_next_rec()){
$rec_num++;
$rec = $dbf->next_rec();
switch ($iblock) {
case "ALTNAMES":
$sql = mysql_query("INSERT INTO $tb_altnames VALUES ('','$rec[1]','$rec[2]','$rec[3]');");
break;
case "DOMA":
$sql = mysql_query("INSERT INTO $tb_doma VALUES ('','$rec[1]','$rec[2]','$rec[3]','$rec[4]','$rec[5]','$rec[6]','$rec[7]','$rec[8]');");
break;
case "FLAT":
$sql = mysql_query("INSERT INTO $tb_flat VALUES ('','$rec[1]','$rec[2]','$rec[3]','$rec[4]','$rec[5]','$rec[6]');");
break;
case "KLADR":
$sql = mysql_query("INSERT INTO $tb_kladr VALUES ('','$rec[1]','$rec[2]','$rec[3]','$rec[4]','$rec[5]','$rec[6]','$rec[7]','$rec[8]');");
break;
case "SOCRBASE":
$sql = mysql_query("INSERT INTO $tb_socrbase VALUES ('','$rec[1]','$rec[2]','$rec[3]','$rec[4]');");
break;
case "STREET":
$sql = mysql_query("INSERT INTO $tb_street VALUES ('','$rec[1]','$rec[2]','$rec[3]','$rec[4]','$rec[5]','$rec[6]','$rec[7]');");
break;
}
if (!$sql) {
echo "<p>Ошибка MySQL №",mysql_errno(),": ",mysql_error(),"</p>";
exit;
}
}
else break;
}
?><script type='text/javascript'>
setTimeout('location.replace("http://<?= $_SERVER[SERVER_NAME],$_SERVER[PHP_SELF]."?rec_num=$rec_num&dir=$dir&progress=$progress&iblock=$iblock&go=$go"; ?>")', 0);
</script><?
exit;
}
}
?>
<form action='<?=$_SERVER['PHP_SELF']?>' method='GET'>
<table>
<tr><td>Директория:</td><td><input class='text' type='text' name='dir' value='/home/thunder/Документы/КЛАДР/base/'></td></tr>
<tr><td>Инфоблок:</td><td> <select class='iblock' name='iblock' size='1'>
<option <?=($_GET[iblock]=="ALTNAMES")?"selected ":"";?> value="ALTNAMES">ALTNAMES</option>
<option <?=($_GET[iblock]=="DOMA")?"selected ":"";?>value="DOMA">DOMA</option>
<option <?=($_GET[iblock]=="FLAT")?"selected ":"";?>value="FLAT">FLAT</option>
<option <?=($_GET[iblock]=="KLADR")?"selected ":"";?>value="KLADR">KLADR</option>
<option <?=($_GET[iblock]=="SOCRBASE")?"selected ":"";?>value="SOCRBASE">SOCRBASE</option>
<option <?=($_GET[iblock]=="STREET")?"selected ":"";?>value="STREET">STREET</option>
</select></td></tr>
<tr><td>Шаг:</td><td><input class='text' type='text' name='go' value=<?=isset($_GET[go])?$_GET[go]:'1000'?>></td></tr>
<tr><td colspan=2 id='submit'><input type='submit' value='Импортировать файл в инфоблок'></td></tr>
</table>
</form>
</div>
<?php
# Подключение к Мускулу
$db_location = "localhost";
$db_conference = "KLADR";
$db_user = "";
$db_password = "";
$db = mysql_connect($db_location,$db_user,$db_password);
if (!$db)
{
echo "<p>Ошибка MySQL №",mysql_errno(),": ",mysql_error(),"</p>";
exit();
}
if (!mysql_select_db($db_conference,$db))
{
echo "<p>Ошибка MySQL №",mysql_errno(),": ",mysql_error(),"</p>";
exit();
}
# Имена таблиц в БД
$tb_altnames = "ALTNAMES";
$tb_doma = "DOMA";
$tb_flat = "FLAT";
$tb_kladr = "KLADR";
$tb_socrbase = "SOCRBASE";
$tb_street = "STREET";
# Установка кодироки запросов к Мускулу
if (!mysql_query("SET NAMES `utf8`"))
{
echo "<p>Ошибка MySQL №",mysql_errno(),": ",mysql_error(),"</p>";
exit();
}
?>
div.iblock{
font-family:Verdana,sans-serif;
font-size:12px;
}
div.iblock table{
border-spacing:1px;
border:solid 1px #B3B3B3;
}
div.iblock td{
border:solid 1px #B3B3B3;
padding:10px;
}
div.iblock input.text{
width:333px;
}
div.iblock #submit{
text-align:center;
}
div.iblock select.iblock{
width:333px;
}