Форум » [x]Harbour » непосредственное изменение имени поля в заголовке dbf » Ответить

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

MIKHAIL: Иногда требуется поменять имя поля без изменения других параметров. Если база 200-300мегов, то обновление занимает долгое время. Есть у кого нибудь опыт изменения имени непосредственно в заголовке DBF что бы не перегонять все данные ?

Ответов - 19

SergKis: MIKHAIL Функции от Pasha к hb 2.0 [pre2] #define HB_OS_WIN_USED #include "hbapi.h" #include "hbapiitm.h" #include "hbapirdd.h" #include "hbrdddbf.h" #include "hbapicdp.h" //#include "hbdate.h" #ifdef __XHARBOUR__ #define HB_SUCCESS SUCCESS #endif /* HB_FUNC( MILLIS ) { hb_retnl( hb_dateMilliSeconds() ); } */ /* HB_FUNC( DBSETCDP ) { AREAP pArea = hb_rddGetCurrentWorkAreaPointer(); if( pArea && ISCHAR(1) ) { char * pCdp = hb_parc(1); if( pCdp ) pArea->cdPage = hb_cdpFind( (char *) pCdp ); } } */ // ------------------------------- // Static func bMemoGetBlock(nPos) // Return {|| dbGetMemoBlock(nPos)} // ------------------------------- HB_FUNC( DBGETMEMOBLOCK ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); USHORT uiField = hb_parni( 1 ) - 1; #ifdef __XHARBOUR__ LPFIELD pField = pArea->lpFields + uiField; #else LPFIELD pField = pArea->area.lpFields + uiField; #endif char * ptr = ( char * ) pArea->pRecord + pArea->pFieldOffset[uiField]; ULONG ulBlock = 0; if( pField->uiLen == 4 ) ulBlock = HB_GET_LE_UINT32( ptr ); else { BYTE bByte; USHORT uiCount; for( uiCount = 0; uiCount < 10; uiCount++ ) { bByte = ptr[ uiCount ]; if( bByte >= '0' && bByte <= '9' ) ulBlock = ulBlock * 10 + ( bByte - '0' ); } } hb_retnl( ulBlock ); } // --------------------------- // Получить значение поля как строка // if FieldType(nPos) == "C"; xVal := FieldGet(nPos) // else ; xVal := dbGetValue(nPos) // endif // --------------------------- HB_FUNC( DBGETVALUE ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); USHORT uiField = hb_parni( 1 ) - 1; #ifdef __XHARBOUR__ LPFIELD pField = pArea->lpFields + uiField; #else LPFIELD pField = pArea->area.lpFields + uiField; #endif hb_retclen( ( char * ) pArea->pRecord + pArea->pFieldOffset[uiField], pField->uiLen ); } // ----------------------------- // if FieldType(nPos) == "C"; FieldPut(nPos, xVal) // else ; dbPutValue(nPos, xVal) // endif // ----------------------------- HB_FUNC( DBPUTVALUE ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); USHORT uiField = hb_parni( 1 ) - 1; #ifdef __XHARBOUR__ LPFIELD pField = pArea->lpFields + uiField; #else LPFIELD pField = pArea->area.lpFields + uiField; #endif if( ISCHAR(2) && ( hb_parclen(2) <= pField->uiLen ) ) { memcpy( ( char * ) pArea->pRecord + pArea->pFieldOffset[uiField], hb_parc(2), hb_parclen(2) ); pArea->fRecordChanged = TRUE; pArea->fDataFlush = TRUE; } hb_ret(); } // ------------------------ // Вставить запись // nKol := 1; nRec := RecNo() // dbGoto(nRec); dbInsert(nRec, nKol) // ------------------------ HB_FUNC( DBINSERT ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); BOOL bOk = TRUE; if( pArea && ! pArea->fReadonly && ! pArea->fShared ) { ULONG ulRec, ulCount = ISNUM(2) ? hb_parnl(2) : 1; #ifdef __XHARBOUR__ ULONG hFile = pArea->hDataFile; #else HB_FHANDLE hFile = hb_fileHandle( pArea->pDataFile ); #endif if( ISNUM(1) ) ulRec = hb_parnl(1); else SELF_RECNO( ( AREAP ) pArea, &ulRec ); if( ulRec == 0 || ulRec > pArea->ulRecCount ) bOk = FALSE; if( bOk && SELF_GOCOLD( ( AREAP ) pArea ) != HB_SUCCESS ) bOk = FALSE; else { ULONG ulIndex; for( ulIndex = 0; ulIndex < ulCount; ulIndex ++) SELF_APPEND( ( AREAP ) pArea, TRUE ); SELF_FLUSH( ( AREAP ) pArea ); /* pArea->fUpdateHeader = TRUE; pArea->ulRecCount += ulCount; if( SELF_WRITEDBHEADER( ( AREAP ) pArea ) != HB_SUCCESS ) bOk = FALSE; */ if( bOk ) { ULONG ulLen = (pArea->ulRecCount - ulRec) * pArea->uiRecordLen; ULONG ulLen1 = ulCount*pArea->uiRecordLen; // ULONG ulRecNo; char * pData = hb_xgrab( ulLen + 1 ); char * pZero = hb_xgrab( ulLen1 + 1 ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) pArea->uiHeaderLen + ( HB_FOFFSET ) pArea->uiRecordLen * ( HB_FOFFSET ) (ulRec - 1), FS_SET ); hb_fsReadLarge( hFile, pData, ulLen ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) pArea->uiHeaderLen + ( HB_FOFFSET ) pArea->uiRecordLen * ( HB_FOFFSET ) (pArea->ulRecCount - ulCount), FS_SET ); hb_fsReadLarge( hFile, pZero, ulLen1 ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) pArea->uiHeaderLen + ( HB_FOFFSET ) pArea->uiRecordLen * ( HB_FOFFSET ) (ulRec - 1), FS_SET ); hb_fsWriteLarge( hFile, pZero, ulLen1 ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) pArea->uiHeaderLen + ( HB_FOFFSET ) pArea->uiRecordLen * ( HB_FOFFSET ) (ulRec + ulCount - 1), FS_SET ); hb_fsWriteLarge( hFile, pData, ulLen ); hb_xfree( pData ); hb_xfree( pZero ); /* for( ulRecNo = ulRec + ulCount - 1; ulRecNo >= ulRec; ulRecNo --) { SELF_GOTO( ( AREAP ) pArea, ulRecNo ); SELF_SETBLANKRECORD( ( AREAP ) pArea, 2 ); // HB_BLANK_EOF } */ } } if( bOk && SELF_GOTO( ( AREAP ) pArea, ulRec ) != HB_SUCCESS ) bOk = FALSE; } hb_retl( bOk ); } // ---------------------------- // Физическое удаление записей // nKol := 1; nRec := RecNo() // dbGoto(nRec); dbDelRecord(nRec, nKol) // ---------------------------- HB_FUNC( DBDELRECORD ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); BOOL bOk = TRUE; if( pArea && ! pArea->fReadonly && ! pArea->fShared ) { ULONG ulRec, ulCount = ISNUM(2) ? hb_parnl(2) : 1; #ifdef __XHARBOUR__ ULONG hFile = pArea->hDataFile; #else HB_FHANDLE hFile = hb_fileHandle( pArea->pDataFile ); #endif if( ISNUM(1) ) ulRec = hb_parnl(1); else SELF_RECNO( ( AREAP ) pArea, &ulRec ); if( ulRec == 0 || (ulRec + ulCount - 1) > pArea->ulRecCount ) bOk = FALSE; if( bOk && SELF_GOCOLD( ( AREAP ) pArea ) != HB_SUCCESS ) bOk = FALSE; else { char pData; if( (ulRec + ulCount - 1) < pArea->ulRecCount ) { ULONG ulLen = (pArea->ulRecCount - (ulRec + ulCount - 1)) * pArea->uiRecordLen; char * pData = hb_xgrab( ulLen + 1 ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) pArea->uiHeaderLen + ( HB_FOFFSET ) pArea->uiRecordLen * ( HB_FOFFSET ) (ulRec + ulCount - 1), FS_SET ); hb_fsReadLarge( hFile, pData, ulLen ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) pArea->uiHeaderLen + ( HB_FOFFSET ) pArea->uiRecordLen * ( HB_FOFFSET ) (ulRec - 1), FS_SET ); hb_fsWriteLarge( hFile, pData, ulLen ); hb_xfree( pData ); } pArea->fUpdateHeader = TRUE; pArea->ulRecCount -= ulCount; } if( bOk && SELF_WRITEDBHEADER( ( AREAP ) pArea ) != HB_SUCCESS ) bOk = FALSE; if( bOk && SELF_GOTO( ( AREAP ) pArea, ulRec ) != HB_SUCCESS ) bOk = FALSE; } hb_retl( bOk ); } // -------------------------- // Обрезать файл после записи ... // nRec := RecNo(); dbGoto(nRec) // dbTruncate(nRec) // -------------------------- HB_FUNC( DBTRUNCATE ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); BOOL bOk = TRUE; if( pArea && ! pArea->fReadonly && ! pArea->fShared ) { ULONG ulRec; if( ISNUM(1) ) ulRec = hb_parnl(1); else SELF_RECNO( ( AREAP ) pArea, &ulRec ); if( ulRec == 0 || ulRec > pArea->ulRecCount ) bOk = FALSE; if( bOk && SELF_GOCOLD( ( AREAP ) pArea ) != HB_SUCCESS ) bOk = FALSE; else { pArea->fUpdateHeader = TRUE; pArea->ulRecCount = ulRec; } if( bOk && SELF_WRITEDBHEADER( ( AREAP ) pArea ) != HB_SUCCESS ) bOk = FALSE; if( bOk && SELF_GOTO( ( AREAP ) pArea, ulRec ) != HB_SUCCESS ) bOk = FALSE; } hb_retl( bOk ); } HB_FUNC( DBRECORDGET ) { AREAP pArea = ( AREAP ) hb_rddGetCurrentWorkAreaPointer(); PHB_ITEM pItem = hb_itemPutNI( NULL, 0 ); BYTE *pRec; if( pArea && (SELF_INFO( (AREAP) pArea, DBI_GETRECSIZE, pItem ) == HB_SUCCESS) && ( SELF_GETREC( (AREAP) pArea, &pRec ) == HB_SUCCESS ) ) { hb_retclen( pRec, hb_itemGetNI(pItem) ); } hb_itemRelease( pItem ); } HB_FUNC( DBRECORDPUT ) { AREAP pArea = ( AREAP ) hb_rddGetCurrentWorkAreaPointer(); PHB_ITEM pItem = hb_itemPutNI( NULL, 0 ); if( pArea && ISCHAR(1) && (SELF_INFO( (AREAP) pArea, DBI_GETRECSIZE, pItem ) == HB_SUCCESS) && hb_parclen(1) >= (ULONG) hb_itemGetNI(pItem) ) { SELF_PUTREC( pArea, hb_parc(1) ); } hb_itemRelease( pItem ); } static void SetFieldType( LPFIELD pField, char bType ) { switch( bType ) { case 'C': pField->uiType = HB_FT_STRING; break; case 'L': pField->uiType = HB_FT_LOGICAL; break; case 'D': pField->uiType = HB_FT_DATE; break; case 'I': case '2': case '4': pField->uiType = HB_FT_INTEGER; break; case 'Y': pField->uiType = HB_FT_CURRENCY; break; case 'N': pField->uiType = HB_FT_LONG; break; case 'F': pField->uiType = HB_FT_FLOAT; break; case '8': case 'B': pField->uiType = HB_FT_DOUBLE; } } // ------------------------ // Переименовать поле, поменять его тип и дробную часть // в пределах длины поля, заданную при создании dbf // FieldRename( nPos, FieldNameNew[, FieldTypeNew[, FieldDecNew]] ) // ------------------------ HB_FUNC( FIELDRENAME ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); USHORT uiField = hb_parni( 1 ); char szFieldName[12]; #ifdef __XHARBOUR__ if( uiField && uiField <= pArea->uiFieldCount && ISCHAR(2) && hb_parclen(2) <= 11 ) #else if( uiField && uiField <= pArea->area.uiFieldCount && ISCHAR(2) && hb_parclen(2) <= 11 ) #endif { char * szType = hb_parc( 3 ); #ifdef __XHARBOUR__ ULONG hFile = pArea->hDataFile; LPFIELD pField = pArea->lpFields + uiField - 1; #else HB_FHANDLE hFile = hb_fileHandle( pArea->pDataFile ); LPFIELD pField = pArea->area.lpFields + uiField - 1; #endif ULONG ulOffset = sizeof( DBFHEADER ) + (uiField-1)*sizeof( DBFFIELD ); memset(szFieldName, 0, 12); memcpy(szFieldName, hb_parc(2), hb_parclen(2) ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) ulOffset, FS_SET ); hb_fsWriteLarge( hFile, szFieldName, 11 ); pField->sym = ( void * ) hb_dynsymGetCase( szFieldName ); if( szType ) { hb_fsSeekLarge( hFile, ( HB_FOFFSET ) ( ulOffset + 11 ), FS_SET ); hb_fsWriteLarge( hFile, szType, 1 ); SetFieldType( pField, szType[0] ); } if( ISNUM( 4 ) ) { USHORT uiDec = hb_parni( 4 ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) ( ulOffset + 17 ), FS_SET ); hb_fsWriteLarge( hFile, (char *) &uiDec, 1 ); pField->uiDec = uiDec; } } } HB_FUNC( DBFRECCOUNT ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); if( pArea ) { if( ISNUM( 1 ) ) { HB_PUT_LE_UINT32( pArea->dbfHeader.ulRecCount, hb_parnl( 1 ) ); pArea->fUpdateHeader = TRUE; } hb_retnl( HB_GET_LE_UINT32( pArea->dbfHeader.ulRecCount ) ); } else hb_retnl( 0 ); } #include <windows.h> HB_FUNC( GETSYSTEMDEFAULTLANGID ) { hb_retnl( GetSystemDefaultLangID() ); } [/pre2]

MIKHAIL: SergKis пишет: FieldRename( nPos, FieldNameNew[, FieldTypeNew[, FieldDecNew]] ) А как использовать ? Я правильно понимаю, что нужно сначала открыть файл например через DBFNTX в эксклюзивном режиме (через ADS наверное работать не будет...) ?

Dima: MIKHAIL пишет: (через ADS наверное работать не будет...) Пробовать надо


SergKis: MIKHAIL пишет Я правильно понимаю, что нужно сначала открыть файл например через DBFNTX в эксклюзивном режиме Именно так. Пробовал с hb 2.0 все ф-ии - работают. На 3.2 DBDELRECORD стала валить программу. В использовании остались только FIELDRENAME - для не больших файлов (определение реальной дробной части и "чужие" имена полей -> в свои) DBINSERT - для использования в файлах отчета (вставка в нужные места итоговых строк, с учетом групп, подгрупп и т.д.) С ADS не работаю.

Haz: менял тупо открывая файл в двоичном режиме ( Fopen() ) и записывая новое название ( и тип если требовалось ) прямо в заголовок Для dbf длинна имени поля не более 10 , заканчивается нулевым байтом. Меняется только так, мгновенно и без оглядки на размер базы В ADS может не прокатить только если используются словари - тк структура в них или база зашифрована

MIKHAIL: Haz пишет: Fopen() ) и записывая новое название чисто теоретически понимаю что можно в бинарном режиме в заголовке вписать новое имя поля, просто на грабли наступать не хочется... Насколько помню первые 32 байта - заголовок, потом идут по 32 байта описание полей., из которых первые 10 отводятся под имя поля, куда и нужно записать коды символов имени. В бинарном режиме опыта работы мало. Какие коды писать ? нужно ли переводить в другую систему или как есть писать...?

Haz: MIKHAIL пишет: Какие коды писать ? нужно ли переводить в другую систему или как есть писать...? Все верно , начиная с 32 байта , каждые 32 последующие это описания полей. Описание полей заканчиваются значением 0Dh Никакая система счисление не требуется , просто пишем новое имя поля латиницей в верхнем регистре. Имя дополняем справа до длинны 10 символом chr(0) например пишем в качестве имени первого поля NEW_NAME [pre2] func main() local cName := space(10) local n := FOPEN( 'demo.dbf', 2 ) // читаем имя первого поля fseek( n, 32, 0 ) fread(n, @cName, 10) ? CharRem( chr(0), cName) // пишем новое имя первому полю fseek( n, 32, 0 ) cName := PADR( "NEW_NAME" , 10, chr(0) ) fWrite( n, cName, 10 ) fClose(n) return nil [/pre2]

MIKHAIL: Haz то что надо! Не могу только разобраться с размером заголовка. По идее он прописан в 8-9 байтах я считаю: headersize=8байт+9байт*255 но не всегда выходит нужное число, проверяемое обратным методом: headersize = кол-во полей * 32 +33 что не так делаю ?

Haz: MIKHAIL пишет: что не так делаю ? эавтра с компа пример сброшу. С телефона неудобно

Dima: может это поможет http://www.realcoding.net/articles/struktura-dbf-failov-dlya-neprodvinutykh.html

Andrey: Всем привет ! Я не совсем понимаю зачем нужно переименовывать поле в базе ? Просто интересно.

Haz: MIKHAIL пишет: я считаю: headersize=8байт+9байт*255 длинна заголовка в байтах получается так ( [pre2] fSeek( n, 8) cBuf := Space(2) fRead( n, @cBuf, hb_BLen(cBuf) ) ? "Header size :" , Bin2L( cBuf ) [/pre2]

MIKHAIL: Dima пишет: может это поможет http://www.realcoding.net/articles/struktura-dbf-failov-dlya-neprodvinutykh.html Это я знаю, тут то же самое: Этот же размер можно извлечь из 8,9 байтов заголовка - HeaderSize У меня значения байтов не сходиться почему то. Наверное не правильно вычисляю размер... Andrey пишет: Я не совсем понимаю зачем нужно переименовывать поле в базе ? Потребность возникла при обновлении больших сетевых фалов, если требуется изменить имя поля. Обычно создается новая структура и туда перегоняются данные. Это очень медленно. Если можно изменить имя поля в заголовке, без изменения структуры данных, это очень быстро. Причины для изменения могут быть у каждого свои.... Я например сделал имя которое в дальнейшем начало конфликтовать с другими подсистемами, и мне проще его поменять в заголовке чем гонять данные... Haz пишет: Bin2L( cBuf ) Попробую, спасибо.

Pasha: MIKHAIL пишет: headersize=8байт+9байт*255 Умножать надо на 256. А лучше считать 2 байта и передать их функции Bin2I. Bin2L это для 4-х байт

Haz: Pasha пишет: Bin2L это для 4-х байт Да, упустил. Всё верно Bin2I

MIKHAIL: Haz пишет: Всё верно Bin2I Все работает, только почему то расчет кол-ва полей отличается от описанной структуры dbf кол-во полей получается по формуле (headersize-34)/32 А символ окончания заголовка - chr(13) находится по адресу headersize - 2

Haz: MIKHAIL пишет: А символ окончания заголовка - chr(13) находится по адресу headersize - 2 В некоторых источниках то что хранится в в 08-09 называют адресом первой записи. Может в этом дело , да и счет начинается с нулевого адреса ( а по сути он первый ) PS не проверял но вроде сходится Пусть имеем 100 полей тогда сам заголовок 32 байта + 100 дескриптор полей по 32 + терминатор = 32 + 100*32 + 1 = 3233 ( занято ) тогда в 8-9 должен быть записан адрес первой записи как 3234

Haz: Haz пишет: не проверял но вроде сходится проверил по какой то dbf c двумя полями в 8-9 записано 98 считаем 32 + 2*32 + 1 = 97 значит первая запись начнется с 98

Pasha: MIKHAIL пишет: Все работает, только почему то расчет кол-ва полей отличается от описанной структуры dbf кол-во полей получается по формуле (headersize-34)/32 Немного не так, формула (headersize-32)/32 Посмотрите класс для низкоуровневой работы c dbf, там разбираются разные форматы, и можно обращаться к полям по имени: oDbf:FieldName Ссылка на класс: https://cloud.mail.ru/public/458B/4hTkEJg3a PS Функцию WinToDos замените на HB_ANSITOOEM



полная версия страницы