1. Visão geral
Ao escrever programas de banco de dados em Delphi, muitas vezes estão envolvidas operações de importação e exportação de dados, como: armazenar dados em um grande banco de dados como arquivos portáteis para facilitar a leitura externa; o banco de dados como arquivos de dados, é mais fácil trocar dados dentro e entre programas e evitar as etapas complicadas de troca de dados através da memória. Por exemplo, no programa de relatório geral escrito pelo autor, esse controle é usado como portador de informações de dados. entrega.
2. Ideias básicas
Como controle de armazenamento de datagrama, ele deve ser capaz de armazenar e ler as informações básicas do conjunto de dados (tais como: nome do campo, nome de exibição do campo, tipo de dados do campo, número de registros, número de campos, valor atual do campo especificado no registro especificado, etc.), e deve Fornecer melhores características de embalagem para facilidade de uso.
Com base nisso, o autor utilizou as características orientadas a objetos do Delphi5.0 para projetar e desenvolver o controle de armazenamento de datagramas.
3. Método de implementação
Escreva a seguinte unidade de código:
unidade IbDbArquivo;
interface
Utiliza Windows, SysUtils, Classes, Formulários, Db, DbTables, Diálogos;
Const.
Flag = 'Datagram-Jixing Software Studio';
Tipo
TDsException = Classe(Exceção);
TIbStorage = classe(TComponent)
Privado
FRptTitle: string; //Descrição do datagrama de armazenamento
FPageHead: string; //Descrição do cabeçalho da página
FPageFoot: string; //Descrição dos pés
FFieldNames: TStrings; //Tabela de nomes de campos
FStreamIndex: TStrings; //índice do campo
FStream: TStream; //Stream que armazena o conteúdo do campo
FFieldCount: Integer; //Número de campos
FRecordCount: Integer; //Número de registros
FOpenFlag: Boolean; // Se o stream é criado flag
protegido
procedimento Reset; //Reset---limpa o conteúdo do stream
procedimento SaveHead(ADataSet: TDataSet; Fp: TStream); //Armazenamento de informações do cabeçalho do relatório
procedimento LoadTableToStream(ADataSet: TDataSet); //Armazena dados do registro
procedimento IndexFields(ADataSet: TDataSet); //Salva os nomes dos campos do conjunto de dados na lista
procedimento GetHead(Fp: TFileStream); //Salvar informações do cabeçalho do relatório
procedimento GetIndex(Fp: TFileStream); //Criar índice de fluxo de registro
procedure GetFieldNames(Fp: TFileStream); //Lê a tabela de nomes de campos do stream;
function GetFieldName(AIndex: Integer): string //Obter o nome do campo
função GetFieldDataType (AIndex: Inteiro): TFieldType;
function GetDisplayLabel(AIndex: Integer): string //Obtém o nome de exibição do campo
procedimento SaveFieldToStream(AStream: TStream; AField: TField); //Salva o campo no stream);
function GetFieldValue(ARecordNo, FieldNo: Integer): string //Conteúdo do campo;
público
Construtor Create(AOwner: TComponent);
Destruição do Destruidor;
procedimento Open; //Cria um fluxo para preparar o armazenamento de dados
procedimento SaveToFile(ADataSet: TDataSet; AFileName: string); //Método de armazenamento);
procedimento LoadFromFile(AFileName: string); //Carregar dados
procedimento FieldStream(ARecordNo, FieldNo: Integer; var AStream: TStream);
propriedade FieldNames[Index: Integer]: string read GetFieldName; //nome do campo
propriedade FieldDataTypes[Índice: Inteiro]: TFieldType leitura GetFieldDataType;
propriedade FieldDisplayLabels[Índice: Inteiro]: string lida GetDisplayLabel;
propriedade Fields[RecNo, FieldIndex: Integer]: string lida GetFieldValue;
//propriedade FieldStreams[RecNo, FieldIndex: Integer]: TStream read GetFieldStream;
propriedade RecordCount: leitura inteira FRecordCount gravação FRecordCount;
propriedade FieldCount: leitura inteira FFieldCount gravação FFieldCount;
publicado
propriedade RptTitle: string lida FRptTitle gravação FRptTitle;
propriedade PageHead: string lida FPageHead escreve FPageHead;
propriedade PageFoot: string lida FPageFoot gravação FPageFoot;
fim;
função ReadAChar (AStream: TStream): Char;
função ReadAStr (AStream: TStream): string;
função ReadBStr(AStream: TStream; Tamanho: Inteiro): string;
função ReadAInteger (AStream: TStream): Inteiro;
procedimento WriteAStr(AStream: TStream; AStr: string);
procedimento WriteBStr(AStream: TStream; AStr: string);
procedimento WriteAInteger(AStream: TStream; AInteger: Integer);
Cadastro de procedimento;
implementação
Cadastro de procedimento;
começar
RegisterComponents('Acesso a dados', [TIbStorage]);
fim;
função ReadAChar (AStream: TStream): Char;
Var
AChar: Caráter;
começar
AStream.Read(AChar, 1);
Resultado := AChar;
fim;
função ReadAStr (AStream: TStream): string;
var
Str: Corda;
C: Caráter;
começar
Str := '';
C := ReadAChar(AStream);
Enquanto C <> #0 faça
começar
Str := Str + C;
C := ReadAChar(AStream);
fim;
Resultado := Str;
fim;
função ReadBStr(AStream: TStream; Tamanho: Inteiro): string;
var
Str: Corda;
C: Caráter;
Eu: Inteiro;
começar
Str := '';
Para I := 1 ao tamanho faça
começar
C := ReadAChar(AStream);
Str := Str + C;
fim;
Resultado := Str;
fim;
função ReadAInteger (AStream: TStream): Inteiro;
var
Str: Corda;
C: Caráter;
começar
Resultado:= MaxInt;
Str := '';
C := ReadAChar(AStream);
Enquanto C <> #0 faça
começar
Str := Str + C;
C := ReadAChar(AStream);
fim;
tentar
Resultado:= StrToInt(Str);
exceto
application.MessageBox('A string atual não pode ser convertida em um número inteiro!', 'Erro',
Mb_Ok + Mb_IconError);
fim;
fim;
procedimento WriteAStr(AStream: TStream; AStr: string);
começar
AStream.Write(Ponteiro(AStr)^, Comprimento(AStr) + 1);
fim;
procedimento WriteBStr(AStream: TStream; AStr: string);
começar
AStream.Write(Ponteiro(AStr)^, Comprimento(AStr));
fim;
procedimento WriteAInteger(AStream: TStream; AInteger: Integer);
var
S: corda;
começar
S := IntToStr(AInteger);
WriteAstr(AStream, S);
fim;
Construtor TIbStorage.Create(AOwner: TComponent);
começar
herdado Criar (AOwner);
FOpenFlag := False; //Sinalizador para determinar se o stream foi criado
fim;
Destruidor TIbStorage.Destroy;
começar
se FOpenFlag então
começar
Fstream.Free;
FStreamIndex.Free;
FFieldNames.Free;
fim;
Destruição herdada;
fim;
procedimento TIbStorage.Open;
começar
FOpenFlag := Verdadeiro;
FStream := TMemoryStream.Create;
FStreamIndex := TStringList.Create;
FFieldNames := TStringList.Create;
Reiniciar;
fim;
procedimento TIbStorage.Reset; //redefinir
começar
se FOpenFlag então
começar
FFieldNames.Limpar;
FStreamIndex.Clear;
FStream.Size := 0;
FRptTitle := '';
FPageHead := '';
FPageFoot := '';
FFieldCount := 0;
FRecordCount := 0;
fim;
fim;
//-------Salvar parte dos dados
procedimento TIbStorage.SaveToFile(ADataSet: TDataSet; AFileName: string);
var
Fp: TFileStream;
Eu: Inteiro;
Ch: Caráter;
T1, T2: TDateTime;
Str: sequência;
começar
se não for FOpenFlag então
começar
showmessage('O objeto não está aberto');
Saída;
fim;
tentar
se FileExists(AFileName) então DeleteFile(AFileName);
Fp := TFileStream.Create(AFileName, fmCreate);
Reiniciar;
SaveHead(ADataSet, Fp); //Salvar as informações do cabeçalho---Instruções adicionais
IndexFields(ADataSet); //Salva as informações do campo do conjunto de dados em FFieldName;
LoadTableToStream(ADataSet); //Salva as informações de dados do conjunto de dados
WriteAStr(Fp, FFieldNames.Text); //Armazena informações do nome do campo
Ch := '@';
Fp.Write(Ch, 1);
WriteAStr(Fp, FStreamIndex.Text); //Lista de índices de campos de armazenamento
Ch := '@';
Fp.Write(Ch, 1);
Fp.CopyFrom(FStream, 0);
finalmente
Fp.Livre;
fim;
fim;
procedimento TIbStorage.SaveHead(ADataSet: TDataSet; Fp: TStream);
Var
Eu: Inteiro;
Ch: Caráter;
começar
se não for ADataSet.Active então ADataSet.Active := True;
WriteAStr(Fp, Bandeira);
WriteAStr(Fp, FRptTitle);
WriteAStr(Fp, FPageHead);
WriteAStr(Fp, FPageFoot);
FFieldCount := ADataSet.Fields.Count;
FRecordCount := ADataSet.RecordCount;
WriteAStr(Fp, IntToStr(ADataSet.Fields.Count));
WriteAStr(Fp, IntToStr(ADataSet.RecordCount));
Ch := '@';
Fp.Write(Ch, 1);
fim;
procedimento TIbStorage.IndexFields(ADataSet: TDataSet);
var
Eu: Inteiro;
Campo AF: Campo TF;
começar
Para I := 0 para ADataSet.Fields.Count - 1 faça
começar
AField := ADataSet.Fields[I];
//Não usar FFieldNames.Values[AField.FieldName] := AField.DisplayLabel é considerar a eficiência;
FFieldNames.Add(AField.FieldName + '=' + AField.DisplayLabel);
FFieldNames.Add(AField.FieldName + 'DataType=' + IntToStr(Ord(AField.DataType)));
fim;
fim;
procedimento TIbStorage.LoadTableToStream(ADataSet: TDataSet);
var
Não: Inteiro;
I, J, Tamanho: Inteiro;
Tmp, Id, Str: string; //id=string(RecNO) + string(FieldNo)
Len: Inteiro;
Ch: Caráter;
BlobStream: TBlobStream;
começar
se não for FOpenFlag então
começar
showmessage('O objeto não está aberto');
Saída;
fim;
tentar
ADataSet.DisableControls;
ADataSet.First;
Não := 0;
FStreamIndex.Clear;
FStream.Size := 0;
Embora não seja ADataSet.Eof
começar
Não := Não + 1;
Para J := 0 para ADataSet.Fields.Count - 1 faça
começar
Id := Inttostr(NO) + '_' + IntToStr(J);
//Cria um índice da posição do stream, o índice aponta para: Size#0Content
FStreamIndex.Add(Id + '=' + IntToStr(FStream.Position));
//Armazena informações de campo no stream
SaveFieldToStream(FStream, ADataSet.Fields[J]);
fim;
ADataSet.Next;
fim;
finalmente
ADataSet.EnableControls;
fim;
fim;
//Se o conteúdo atual de um campo estiver vazio ou BlobSize<=0, apenas o tamanho do campo será 0 e nenhum conteúdo será gravado.
procedimento TIbStorage.SaveFieldToStream(AStream: TStream; AField: TField);
var
Tamanho: Inteiro;
Ch: Caráter;
XF: TStream;
Str: sequência;
começar
se AField.IsBlob então
começar
//Como armazenar o conteúdo de um campo TBlobField como um stream
Xf := TBlobStream.Create(TBlobField(AField), bmread);
tentar
se Xf.Size > 0 então
começar
Tamanho := Xf.Tamanho;
WriteAInteger(AStream, Tamanho);
AStream.CopyFrom(Xf, Xf.Size);
fim
outro
WriteAInteger(AStream, 0);
finalmente
XF.Grátis;
fim;
fim
outro
começar
Str := AField.AsString;
Tamanho := Comprimento(Str);
WriteAInteger(AStream, Tamanho);
se Tamanho <> 0 então
AStream.Write(Ponteiro(Str)^, Tamanho);
//WriteAstr(AStream, Str);
fim;
Ch := '@';
AStream.Write(Cap, 1);
fim;
//------------Carregar dados
procedimento TIbStorage.LoadFromFile(AFileName: string);
var
Fp: TFileStream;
Verifique: string;
começar
Reiniciar;
tentar
se Not FileExists(AFileName) então
começar
showmessage('Arquivo não existe:' + AFileName);
Saída;
fim;
Fp := TFileStream.Create(AFileName, fmOpenRead);
Verifique := ReadAStr(Fp);
se verificar <> sinalizar então
começar
Application.MessageBox('Formato de arquivo ilegal', 'Erro', Mb_Ok + Mb_IconError);
Saída;
fim;
ObterCabeça(Fp);
GetFieldNames(Fp);
GetIndex(Fp);
FStream.CopyFrom(Fp, Fp.Size-Fp.Position);
finalmente
Fp.Livre;
fim;
fim;
procedimento TIbStorage.GetHead(Fp: TFileStream);
começar
FRptTitle := ReadAStr(Fp);
FPageHead := ReadAstr(Fp);
FPageFoot := ReadAstr(Fp);
FFieldCount := ReadAInteger(Fp);
FRecordCount := ReadAInteger(Fp);
se ReadAChar(Fp) <> '@' then showmessage('GetHead File Error');
fim;
procedimento TIbStorage.GetFieldNames(Fp: TFileStream);
var
Ch: Caráter;
Str: sequência;
começar
Str := '';
Str := LerAStr(Fp);
FFieldNames.CommaText := Str;
Ch := ReadAChar(Fp);
if Ch <> '@' then Showmessage('Quando obter erro de nomes de campo');
fim;
procedimento TIbStorage.GetIndex(Fp: TFileStream);
var
Ch: Caráter;
Str: sequência;
começar
Str := '';
Str := LerAStr(Fp);
FStreamIndex.CommaText := Str;
Ch := ReadAChar(Fp);
if Ch <> '@' then Showmessage('Quando obter erro no índice de posição do campo');
fim;
//---------Lê a parte do valor do campo
função TIbStorage.GetFieldValue(ARecordNo, FieldNo: Integer): string;
var
Id,T: string;
Pos: Inteiro;
Len, I: Inteiro;
Er: Booleano;
começar
Resultado:= '';
Er := Falso;
se ARecordNo > FRecordCount então
Er := true; //ARecordNo := FRecordCount;
se ARecordNo < 1 então
Er := Verdadeiro; // ARecordNo := 1;
se FieldNo >= FFieldCount então
Er := Verdadeiro; // FieldNo := FFieldCount - 1;
ifFieldNo < 0 então
Er := Verdadeiro; //CampoNo := 0;
se Er então
começar
Showmessage('O número do registro ou rótulo do campo está fora dos limites');
Saída;
fim;
se FFieldCount = 0 então Sair;
Id := Inttostr(ARecordNO) + '_' + IntToStr(FieldNo);
Pos := StrToInt(FStreamIndex.Values[Id]);
FStream.Position := Pos;
//Obtém o comprimento do conteúdo do campo
Len := ReadAInteger(FStream);
se Len > 0 então
Resultado:= ReadBStr(FStream, Len);
se ReadAChar(FStream) <> '@' então
Showmessage('Ao ler o campo, encontra erro de formato para salvar');
fim;
procedimento TIbStorage.FieldStream(ARecordNo, FieldNo: Integer; var AStream: TStream);
var
Id,T: string;
Pos: Inteiro;
Len, I: Inteiro;
Er: Booleano;
começar
Er := Falso;
se ARecordNo > FRecordCount então
Er := true; //ARecordNo := FRecordCount;
se ARecordNo < 1 então
Er := Verdadeiro; // ARecordNo := 1;
se FieldNo >= FFieldCount então
Er := Verdadeiro; // FieldNo := FFieldCount - 1;
ifFieldNo < 0 então
Er := Verdadeiro; //CampoNo := 0;
se Er então
começar
TDsException.Create('Subscrito do índice da função GetFieldValue fora dos limites');
Saída;
fim;
se FFieldCount = 0 então Sair;
Id := Inttostr(ARecordNO) + IntToStr(FieldNo);
Pos := StrToInt(FStreamIndex.Values[Id]);
FStream.Position := Pos;
Len := ReadAInteger(FStream);
AStream.CopyFrom(FStream, Len);
fim;
function TIbStorage.GetFieldName(AIndex: Integer): string //Obter o nome do campo;
começar
//Os campos e tipos de dados armazenados representam metade cada
se ((AIndex < 0) ou (AIndex >= FFieldNames.Count div 2)) então
Application.MessageBox('Índice de nome de campo fora dos limites', 'Erro de programa',
Mb_Ok + Mb_IconError)
outro
Resultado := FFieldNames.Names[AIndex*2];
fim;
function TIbStorage.GetFieldDataType(AIndex: Integer): TFieldType;
começar
//Os campos e tipos de dados armazenados representam metade cada
se ((AIndex < 0) ou (AIndex >= FFieldNames.Count div 2)) então
Application.MessageBox('O índice do tipo de dados do campo está fora dos limites', 'Erro de programa',
Mb_Ok + Mb_IconError)
outro
Resultado:= TFieldType(StrToInt(FFieldNames.Values[FFieldNames.Names[AIndex*2+1]]));
fim;
function TIbStorage.GetDisplayLabel(AIndex: Integer): string //Obter o nome de exibição do campo
começar
se ((AIndex < 0) ou (AIndex >= FFieldNames.Count)) então
Application.MessageBox('Índice de nome de campo fora dos limites', 'Erro de programa',
Mb_Ok + Mb_IconError)
outro
Resultado:= FFieldNames.Values[GetFieldName(AIndex)];
fim;
fim.
Por meio de testes, esse controle tem bom suporte para controles de conjunto de dados comumente usados, como Ttable, Tquery, TaodTable, TadoQuery, TibTable, TibQuery, etc., e tem boa eficiência (teste: 1100 registros pessoais, 23 campos armazenados como arquivo leva aproximadamente 2 segundos).
4. Uso básico de controles
1. Armazenar dados de um conjunto de dados em um arquivo
IbStorage1.Open; //Cria um fluxo de armazenamento
IbStorage1.SaveToFile(AdataSet,Afilename);
2. Ler informações de dados do arquivo
IbStorage1.Open;
IbStorage1.LoadFromFile(AfileName);
3. Acesso aos dados no controle de armazenamento de datagramas
Valor := IbStorage1.Fields[ArecNo, AfieldNo];
Outros são omitidos.
5. Conclusão
Ao escrever este controle de armazenamento de datagramas, o problema de armazenamento e troca de dados em programas de banco de dados é melhor resolvido e um controle prático é fornecido para o desenvolvimento de programas de banco de dados.
O controle passou na depuração no ambiente de desenvolvimento Windows98 e Delphi5.