📄 tabdatfile.cpp
字号:
// tabdatfile.cpp: implementation of the TABDATFile class.////////////////////////////////////////////////////////////////////////#include "tabdatfile.h"#include "ugk_errhandle.h"#include "ugk_memopr.h"#include "ugk_string.h"/********************************************************************** * TABDATFile::TABDATFile() * 构造函数 $zgq * Constructor. **********************************************************************/TABDATFile::TABDATFile(){ m_fp = NULL; m_pszFname = NULL; m_eTableType = TABTableNative; m_poHeaderBlock = NULL; m_poRecordBlock = NULL; m_pasFieldDef = NULL; m_numFields = -1; m_numRecords = -1; m_nFirstRecordPtr = 0; m_nBlockSize = 0; m_nRecordSize = -1; m_nCurRecordId = -1; m_bCurRecordDeletedFlag = FALSE; m_bWriteHeaderInitialized = FALSE;}/********************************************************************** * TABDATFile::~TABDATFile() * 析构函数 $zgq * Destructor. **********************************************************************/TABDATFile::~TABDATFile(){ Close();}/********************************************************************** * TABDATFile::Open() * * Open a .DAT file, and initialize the structures to be ready to read * records from it. * * We currently support NATIVE and DBF tables for reading, and only * NATIVE tables for writing. * * Returns 0 on success, -1 on error. **********************************************************************/int TABDATFile::Open(const char *pszFname, const char *pszAccess, TABTableType eTableType /*=TABNativeTable*/){ int i; if (m_fp) { UGKError(ET_Failure, UGKErr_FileIO, "Open() failed: object already contains an open file"); return -1; } /*----------------------------------------------------------------- * Validate access mode and make sure we use binary access. *----------------------------------------------------------------*/ if (EQUALN(pszAccess, "r", 1) && (eTableType==TABTableNative || eTableType==TABTableDBF) ) { m_eAccessMode = TABRead; pszAccess = "rb"; } else if (EQUALN(pszAccess, "w", 1) && eTableType==TABTableNative) { m_eAccessMode = TABWrite; pszAccess = "wb"; } else { UGKError(ET_Failure, UGKErr_FileIO, "Open() failed: access mode \"%s\" not supported", pszAccess); return -1; } /*----------------------------------------------------------------- * Open file for reading *----------------------------------------------------------------*/ m_pszFname = UGKStrdup(pszFname); m_fp = fopen(m_pszFname, pszAccess); m_eTableType = eTableType; if (m_fp == NULL) { UGKError(ET_Failure, UGKErr_FileIO, "Open() failed for %s", m_pszFname); UGK_Free(m_pszFname); m_pszFname = NULL; return -1; } if (m_eAccessMode == TABRead) { /*------------------------------------------------------------ * READ ACCESS: * Read .DAT file header (record size, num records, etc...) * m_poHeaderBlock will be reused later to read field definition *-----------------------------------------------------------*/ m_poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE); m_poHeaderBlock->ReadFromFile(m_fp, 0, 32); //从头开始读取前32个字节 $zgq m_poHeaderBlock->ReadByte(); // Table type ??? 0x03 m_poHeaderBlock->ReadByte(); // Last update year m_poHeaderBlock->ReadByte(); // Last update month m_poHeaderBlock->ReadByte(); // Last update day m_numRecords = m_poHeaderBlock->ReadInt32(); //总记录数 $zgq m_nFirstRecordPtr = m_poHeaderBlock->ReadInt16(); m_nRecordSize = m_poHeaderBlock->ReadInt16(); //记录长度 $zgq m_numFields = m_nFirstRecordPtr/32 - 1; /*------------------------------------------------------------- * Read the field definitions * First 32 bytes field definition starts at byte 32 in file *------------------------------------------------------------*/ m_pasFieldDef = (TABDATFieldDef*)UGK_Calloc(m_numFields, sizeof(TABDATFieldDef)); for(i=0; i<m_numFields; i++) { m_poHeaderBlock->GotoByteInFile((i+1)*32); m_poHeaderBlock->ReadBytes(11, (UGKByte*)m_pasFieldDef[i].szName); m_pasFieldDef[i].szName[10] = '\0'; m_pasFieldDef[i].cType = (char)m_poHeaderBlock->ReadByte(); m_poHeaderBlock->ReadInt32(); // Skip Bytes 12-15 m_pasFieldDef[i].byLength = m_poHeaderBlock->ReadByte(); m_pasFieldDef[i].byDecimals = m_poHeaderBlock->ReadByte(); m_pasFieldDef[i].eTABType = TABFUnknown; } /*------------------------------------------------------------- * Establish a good record block size to use based on record size, and * then create m_poRecordBlock * Record block size has to be a multiple of record size. *------------------------------------------------------------*/ m_nBlockSize = ((1024/m_nRecordSize)+1)*m_nRecordSize; m_nBlockSize = MIN(m_nBlockSize, (m_numRecords*m_nRecordSize)); assert( m_poRecordBlock == NULL ); m_poRecordBlock = new TABRawBinBlock(m_eAccessMode, FALSE); m_poRecordBlock->InitNewBlock(m_fp, m_nBlockSize); m_poRecordBlock->SetFirstBlockPtr(m_nFirstRecordPtr); } else { /*------------------------------------------------------------ * WRITE ACCESS: * Set acceptable defaults for all class members. * The real header initialization will be done when the first * record is written *-----------------------------------------------------------*/ m_poHeaderBlock = NULL; m_numRecords = 0; m_nFirstRecordPtr = 0; m_nRecordSize = 0; m_numFields = 0; m_pasFieldDef = NULL; m_bWriteHeaderInitialized = FALSE; } return 0;}/********************************************************************** * TABDATFile::Close() * * Close current file, and release all memory used. * * Returns 0 on success, -1 on error. **********************************************************************/int TABDATFile::Close(){ if (m_fp == NULL) return 0; /*---------------------------------------------------------------- * Write access: Update the header with number of records, etc. * and add a CTRL-Z char at the end of the file. *---------------------------------------------------------------*/ if (m_eAccessMode == TABWrite) { WriteHeader(); char cEOF = 26; if (fseek(m_fp, 0L, SEEK_END) == 0) fwrite(&cEOF, 1, 1, m_fp); } // Delete all structures if (m_poHeaderBlock) { delete m_poHeaderBlock; m_poHeaderBlock = NULL; } if (m_poRecordBlock) { delete m_poRecordBlock; m_poRecordBlock = NULL; } // Close file fclose(m_fp); m_fp = NULL; UGK_Free(m_pszFname); m_pszFname = NULL; UGK_Free(m_pasFieldDef); m_pasFieldDef = NULL; m_numFields = -1; m_numRecords = -1; m_nFirstRecordPtr = 0; m_nBlockSize = 0; m_nRecordSize = -1; m_nCurRecordId = -1; m_bWriteHeaderInitialized = FALSE; return 0;}/********************************************************************** * TABDATFile::InitWriteHeader() * * Init the header members to be ready to write the header and data records * to a newly created data file. * * Returns 0 on success, -1 on error. **********************************************************************/int TABDATFile::InitWriteHeader(){ int i; if (m_eAccessMode != TABWrite || m_bWriteHeaderInitialized) return 0; /*------------------------------------------------------------ * Compute values for Record size, header size, etc. *-----------------------------------------------------------*/ m_nFirstRecordPtr = (m_numFields+1)*32 + 1; m_nRecordSize = 1; for(i=0; i<m_numFields; i++) { m_nRecordSize += m_pasFieldDef[i].byLength; } /*------------------------------------------------------------- * Create m_poRecordBlock the size of a data record. *------------------------------------------------------------*/ m_nBlockSize = m_nRecordSize; assert( m_poRecordBlock == NULL ); m_poRecordBlock = new TABRawBinBlock(m_eAccessMode, FALSE); m_poRecordBlock->InitNewBlock(m_fp, m_nBlockSize); m_poRecordBlock->SetFirstBlockPtr(m_nFirstRecordPtr); /*------------------------------------------------------------- * Make sure this init. will be performed only once *------------------------------------------------------------*/ m_bWriteHeaderInitialized = TRUE; return 0;}/********************************************************************** * TABDATFile::WriteHeader() * * Init the header members to be ready to write the header and data records * to a newly created data file. * * Returns 0 on success, -1 on error. **********************************************************************/int TABDATFile::WriteHeader(){ int i; if (m_eAccessMode != TABWrite) { UGKError(ET_Failure, UGKErr_NotSupported, "WriteHeader() can be used only with Write access."); return -1; } if (!m_bWriteHeaderInitialized) InitWriteHeader(); /*------------------------------------------------------------ * Create a single block that will be used to generate the whole header. *-----------------------------------------------------------*/ if (m_poHeaderBlock == NULL) m_poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE); m_poHeaderBlock->InitNewBlock(m_fp, m_nFirstRecordPtr, 0); /*------------------------------------------------------------ * First 32 bytes: main header block *-----------------------------------------------------------*/ m_poHeaderBlock->WriteByte(0x03); // Table type ??? 0x03 // __TODO__ Write the correct update date value m_poHeaderBlock->WriteByte(99); // Last update year m_poHeaderBlock->WriteByte(9); // Last update month m_poHeaderBlock->WriteByte(9); // Last update day m_poHeaderBlock->WriteInt32(m_numRecords); m_poHeaderBlock->WriteInt16(m_nFirstRecordPtr); m_poHeaderBlock->WriteInt16(m_nRecordSize); m_poHeaderBlock->WriteZeros(20); // Pad rest with zeros /*------------------------------------------------------------- * Field definitions follow. Each field def is 32 bytes. *------------------------------------------------------------*/ for(i=0; i<m_numFields; i++) { m_poHeaderBlock->WriteBytes(11, (UGKByte*)m_pasFieldDef[i].szName); m_poHeaderBlock->WriteByte(m_pasFieldDef[i].cType); m_poHeaderBlock->WriteInt32(0); // Skip Bytes 12-15 m_poHeaderBlock->WriteByte(m_pasFieldDef[i].byLength); m_poHeaderBlock->WriteByte(m_pasFieldDef[i].byDecimals); m_poHeaderBlock->WriteZeros(14); // Pad rest with zeros } /*------------------------------------------------------------- * Header ends with a 0x0d character. *------------------------------------------------------------*/ m_poHeaderBlock->WriteByte(0x0d); /*------------------------------------------------------------- * Write the block to the file and return. *------------------------------------------------------------*/ return m_poHeaderBlock->CommitToFile();}/********************************************************************** * TABDATFile::GetNumFields() * * Return the number of fields in this table. * * Returns a value >= 0 on success, -1 on error. **********************************************************************/int TABDATFile::GetNumFields(){ return m_numFields;}/********************************************************************** * TABDATFile::GetNumRecords() * * Return the number of records in this table. * * Returns a value >= 0 on success, -1 on error. **********************************************************************/int TABDATFile::GetNumRecords(){ return m_numRecords;}/********************************************************************** * TABDATFile::GetRecordBlock() * * Return a TABRawBinBlock reference positioned at the beginning of the * specified record and ready to read (or write) field values from/to it. * In read access, the returned block is guaranteed to contain at least one * full record of data, and in write access, it is at least big enough to * hold one full record. * * Note that record ids are positive and start at 1. * * In Write access, CommitRecordToFile() MUST be called after the * data items have been written to the record, otherwise the record * will never make it to the file. * * Returns a reference to the TABRawBinBlock on success or NULL on error. * The returned pointer is a reference to a block object owned by this * TABDATFile object and should not be freed by the caller. **********************************************************************/TABRawBinBlock *TABDATFile::GetRecordBlock(int nRecordId){ m_bCurRecordDeletedFlag = FALSE; if (m_eAccessMode == TABRead) { /*------------------------------------------------------------- * READ ACCESS *------------------------------------------------------------*/ int nFileOffset; nFileOffset = m_nFirstRecordPtr+(nRecordId-1)*m_nRecordSize; /*------------------------------------------------------------- * Move record block pointer to the right location *------------------------------------------------------------*/ if ( m_poRecordBlock == NULL || nRecordId < 1 || nRecordId > m_numRecords || m_poRecordBlock->GotoByteInFile(nFileOffset) != 0 ) { UGKError(ET_Failure, UGKErr_FileIO, "Failed reading .DAT record block for record #%d in %s", nRecordId, m_pszFname); return NULL; } /*------------------------------------------------------------- * The first char of the record is a ' ' for an active record, or * '*' for a deleted one. * In the case of a deleted record, we simply return default * values for each attribute... this is what MapInfo seems to do * when it takes a .TAB with deleted records and exports it to .MIF *------------------------------------------------------------*/ if (m_poRecordBlock->ReadByte() != ' ') { m_bCurRecordDeletedFlag = TRUE; } } else if (m_eAccessMode == TABWrite && nRecordId > 0) { /*------------------------------------------------------------- * WRITE ACCESS *------------------------------------------------------------*/ int nFileOffset; /*------------------------------------------------------------- * Before writing the first record, we must generate the file * header. We will also initialize class members such as record * size, etc. and will create m_poRecordBlock. *------------------------------------------------------------*/ if (!m_bWriteHeaderInitialized) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -