1. 개요
Delphi에서 데이터베이스 프로그램을 작성할 때 다음과 같은 데이터 가져오기 및 내보내기 작업이 종종 포함됩니다. 외부에서 쉽게 읽을 수 있도록 대용량 데이터베이스에 데이터를 저장합니다. 또한 파일에 저장된 데이터 정보를 다른 데이터베이스에 저장합니다. 데이터베이스를 데이터 파일로 사용하면 프로그램 내에서나 프로그램 간에 데이터를 교환하는 것이 더 쉽고 메모리를 통해 데이터를 교환하는 번거로운 단계를 피할 수 있습니다. 예를 들어 저자가 작성한 일반 보고서 프로그램에서 이 컨트롤은 데이터 정보 매체로 사용됩니다. 배달.
2. 기본 아이디어
데이터그램 저장 제어로서 데이터 세트의 기본 정보(예: 필드 이름, 필드 표시 이름, 필드 데이터 유형, 레코드 수, 필드 수, 지정된 필드의 현재 값)를 저장하고 읽을 수 있어야 합니다. 지정된 기록 등), 사용 편의성을 위해 더 나은 포장 특성을 제공할 수 있어야 합니다.
이를 바탕으로 저자는 Delphi5.0의 객체지향적 특성을 활용하여 데이터그램 저장 제어를 설계하고 개발하였다.
3. 시행방법
다음 코드 단위를 작성합니다.
단위 IbDbFile;
인터페이스
Windows, SysUtils, 클래스, 양식, Db, DbTables, 대화 상자를 사용합니다.
상수
플래그 = '데이터그램-지싱 소프트웨어 스튜디오';
유형
TDsException = 클래스(예외);
TIbStorage = 클래스(TComponent)
사적인
FRptTitle: 문자열; //저장소 데이터그램 설명
FPageHead: 문자열; //페이지 헤더 설명
FPageFoot: string; //발에 대한 설명
FFieldNames: TStrings; //필드 이름 테이블
FStreamIndex: TStrings;
FStream: TStream; //필드 내용을 저장하는 스트림
FFieldCount: 정수 //필드 수;
FRecordCount: 정수 //레코드 수;
FOpenFlag: Boolean; // 스트림 생성 여부 플래그
보호됨
절차 재설정; //재설정---스트림의 내용을 지웁니다.
절차 SaveHead(ADataSet: TDataSet; Fp: TStream); //스토리지 보고서 헤더 정보
절차 LoadTableToStream(ADataSet: TDataSet); //저장소 레코드 데이터
Procedure IndexFields(ADataSet: TDataSet); //데이터 세트의 필드 이름을 목록에 저장합니다.
절차 GetHead(Fp: TFileStream); //보고서 헤더 정보 저장
절차 GetIndex(Fp: TFileStream); //레코드 스트림 인덱스 생성
절차 GetFieldNames(Fp: TFileStream); //스트림에서 필드 이름 테이블을 읽습니다.
function GetFieldName(AIindex: Integer): string //필드 이름 가져오기;
함수 GetFieldDataType(AIindex: Integer): TFieldType;
function GetDisplayLabel(AIndex: Integer): string //필드 표시 이름 가져오기;
절차 SaveFieldToStream(AStream: TStream; AField: TField); //필드를 스트림에 저장합니다.
function GetFieldValue(ARecordNo, FieldNo: Integer): 문자열; //필드 내용
공공의
생성자 Create(AOwner: TComponent);
소멸자 재정의;
Procedure Open; //데이터 저장을 준비하기 위한 스트림을 생성합니다.
절차 SaveToFile(ADataSet: TDataSet; AFileName: 문자열); //저장 방법
절차 LoadFromFile(AFileName: string); //데이터 로드
절차 FieldStream(ARecordNo, FieldNo: 정수; var AStream: TStream);
property FieldNames[Index: Integer]: 문자열 읽기 GetFieldName //필드 이름;
속성 FieldDataTypes[Index: Integer]: TFieldType read GetFieldDataType;
속성 FieldDisplayLabels[Index: Integer]: 문자열 읽기 GetDisplayLabel;
속성 Fields[RecNo, FieldIndex: Integer]: 문자열 읽기 GetFieldValue;
//속성 FieldStreams[RecNo, FieldIndex: Integer]: TStream read GetFieldStream;
속성 RecordCount: 정수 읽기 FRecordCount 쓰기 FRecordCount;
속성 FieldCount: 정수 읽기 FFieldCount 쓰기 FFieldCount;
출판됨
속성 RptTitle: 문자열 읽기 FRptTitle 쓰기 FRptTitle;
속성 PageHead: 문자열 읽기 FPageHead 쓰기 FPageHead;
속성 PageFoot: 문자열 읽기 FPageFoot 쓰기 FPageFoot;
끝;
함수 ReadAChar(AStream: TStream): Char;
함수 ReadAStr(AStream: TStream): 문자열;
함수 ReadBStr(AStream: TStream; 크기: 정수): 문자열;
함수 ReadAInteger(AStream: TStream): 정수;
절차 WriteAStr(AStream: TStream; AStr: 문자열);
절차 WriteBStr(AStream: TStream; AStr: 문자열);
절차 WriteAInteger(AStream: TStream; AInteger: Integer);
절차 등록;
구현
절차 등록;
시작하다
RegisterComponents('데이터 접근', [TIbStorage]);
끝;
함수 ReadAChar(AStream: TStream): Char;
바르
AChar: 문자;
시작하다
AStream.Read(AChar, 1);
결과 := AChar;
끝;
함수 ReadAStr(AStream: TStream): 문자열;
var
Str: 문자열;
C: 숯;
시작하다
Str := '';
C := ReadAChar(AStream);
C <> #0 하는 동안
시작하다
Str := Str + C;
C := ReadAChar(AStream);
끝;
결과 := Str;
끝;
함수 ReadBStr(AStream: TStream; 크기: 정수): 문자열;
var
Str: 문자열;
C: 숯;
I : 정수;
시작하다
Str := '';
I의 경우 := 1 ~ 크기
시작하다
C := ReadAChar(AStream);
Str := Str + C;
끝;
결과 := Str;
끝;
함수 ReadAInteger(AStream: TStream): 정수;
var
Str: 문자열;
C:숯;
시작하다
결과 := MaxInt;
Str := '';
C := ReadAChar(AStream);
C <> #0 하는 동안
시작하다
Str := Str + C;
C := ReadAChar(AStream);
끝;
노력하다
결과 := StrToInt(Str);
제외하고
application.MessageBox('현재 문자열을 정수로 변환할 수 없습니다!', '오류',
Mb_Ok + Mb_IconError);
끝;
끝;
절차 WriteAStr(AStream: TStream; AStr: 문자열);
시작하다
AStream.Write(Pointer(AStr)^, 길이(AStr) + 1);
끝;
절차 WriteBStr(AStream: TStream; AStr: 문자열);
시작하다
AStream.Write(Pointer(AStr)^, 길이(AStr));
끝;
절차 WriteAInteger(AStream: TStream; AInteger: Integer);
var
S: 문자열;
시작하다
S := IntToStr(AIInteger);
WriteAstr(AStream, S);
끝;
생성자 TIbStorage.Create(AOwner: TComponent);
시작하다
상속받은 Create(AOwner);
FOpenFlag := False; //스트림 생성 여부를 결정하는 플래그
끝;
소멸자 TIbStorage.Destroy;
시작하다
FOpenFlag이면
시작하다
FStream.Free;
FStreamIndex.Free;
FFieldNames.Free;
끝;
상속됨 파괴;
끝;
프로시저 TIBStorage.Open;
시작하다
FOpenFlag := 참;
FStream := TMemoryStream.Create;
FStreamIndex := TStringList.Create;
FFieldNames := TStringList.Create;
다시 놓기;
끝;
절차 TIbStorage.Reset;
시작하다
FOpenFlag이면
시작하다
FFieldNames.Clear;
FStreamIndex.Clear;
FStream.Size := 0;
FRptTitle := '';
FPageHead := '';
FPageFoot := '';
FFieldCount := 0;
FRecordCount := 0;
끝;
끝;
//---------데이터 저장 부분
절차 TIbStorage.SaveToFile(ADataSet: TDataSet; AFileName: 문자열);
var
Fp: TFileStream;
I : 정수;
Ch: 문자;
T1, T2: TDateTime;
Str: 문자열;
시작하다
FOpenFlag가 아니면
시작하다
showmessage('객체가 열려있지 않습니다.');
출구;
끝;
노력하다
if FileExists(AFileName) then DeleteFile(AFileName);
Fp := TFileStream.Create(AFileName, fmCreate);
다시 놓기;
SaveHead(ADataSet, Fp); //헤더 정보 저장---추가 명령
IndexFields(ADataSet); //FFieldName에 설정된 데이터의 필드 정보를 저장합니다.
LoadTableToStream(ADataSet); //데이터 세트의 데이터 정보를 저장합니다.
WriteAStr(Fp, FFieldNames.Text); //저장소 필드 이름 정보
Ch := '@';
Fp.Write(Ch, 1);
WriteAStr(Fp, FStreamIndex.Text); //저장 필드 인덱스 목록
Ch := '@';
Fp.Write(Ch, 1);
Fp.CopyFrom(FStream, 0);
마지막으로
Fp.무료;
끝;
끝;
절차 TIbStorage.SaveHead(ADataSet: TDataSet; Fp: TStream);
바르
I : 정수;
Ch: 문자;
시작하다
ADataSet.Active가 아닌 경우 ADataSet.Active := True;
WriteAStr(Fp, 플래그);
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);
끝;
절차 TIbStorage.IndexFields(ADataSet: TDataSet);
var
I : 정수;
AField: T필드;
시작하다
I의 경우 := 0에서 ADataSet.Fields.Count - 1 do
시작하다
AField := ADataSet.Fields[I];
//FFieldNames.Values[AField.FieldName] := AField.DisplayLabel을 사용하지 않는 것은 효율성을 고려하는 것입니다.
FFieldNames.Add(AField.FieldName + '=' + AField.DisplayLabel);
FFieldNames.Add(AField.FieldName + 'DataType=' + IntToStr(Ord(AField.DataType)));
끝;
끝;
절차 TIbStorage.LoadTableToStream(ADataSet: TDataSet);
var
아니요: 정수;
I, J, 크기: 정수;
Tmp, Id, Str : 문자열; //id=string(RecNO) + 문자열(FieldNo)
렌: 정수;
Ch: 문자;
BlobStream: TBlobStream;
시작하다
FOpenFlag가 아니면
시작하다
showmessage('객체가 열려있지 않습니다.');
출구;
끝;
노력하다
ADataSet.DisableControls;
ADataSet.First;
아니요 := 0;
FStreamIndex.Clear;
FStream.Size := 0;
ADataSet.Eof가 아닌 동안
시작하다
아니요 := 아니요 + 1;
J의 경우 := 0에서 ADataSet.Fields.Count - 1 do
시작하다
Id := Inttostr(NO) + '_' + IntToStr(J);
//스트림 위치의 인덱스를 생성합니다. 인덱스는 다음을 가리킵니다: Size#0Content
FStreamIndex.Add(Id + '=' + IntToStr(FStream.Position));
//스트림에 저장 필드 정보
SaveFieldToStream(FStream, ADataSet.Fields[J]);
끝;
ADataSet.Next;
끝;
마지막으로
ADataSet.EnableControls;
끝;
끝;
//필드의 현재 콘텐츠가 비어 있거나 BlobSize<=0인 경우 필드 크기만 0이고 콘텐츠가 기록되지 않습니다.
절차 TIbStorage.SaveFieldToStream(AStream: TStream; AField: TField);
var
크기: 정수;
Ch: 문자;
XF: T스트림;
Str: 문자열;
시작하다
AField.IsBlob이면
시작하다
//TBlobField 필드의 내용을 스트림으로 저장하는 방법
Xf := TBlobStream.Create(TBlobField(AField), bmread);
노력하다
Xf.Size > 0이면
시작하다
크기 := Xf.Size;
WriteAInteger(AStream, 크기);
AStream.CopyFrom(Xf, Xf.Size);
끝
또 다른
WriteAInteger(AStream, 0);
마지막으로
XF.무료;
끝;
끝
또 다른
시작하다
Str := AField.AsString;
크기 := 길이(Str);
WriteAInteger(AStream, 크기);
크기 <> 0이면
AStream.Write(Pointer(Str)^, 크기);
//WriteAstr(AStream, Str);
끝;
Ch := '@';
AStream.Write(Ch, 1);
끝;
//------------데이터 로드
절차 TIbStorage.LoadFromFile(AFileName: 문자열);
var
Fp: TFileStream;
확인: 문자열;
시작하다
다시 놓기;
노력하다
FileExists(AFileName)가 아닌 경우
시작하다
showmessage('파일이 존재하지 않습니다:' + AFileName);
출구;
끝;
Fp := TFileStream.Create(AFileName, fmOpenRead);
확인 := ReadAStr(Fp);
<> 플래그를 선택한 경우
시작하다
Application.MessageBox('잘못된 파일 형식', '오류', Mb_Ok + Mb_IconError);
출구;
끝;
GetHead(Fp);
GetFieldNames(Fp);
GetIndex(Fp);
FStream.CopyFrom(Fp, Fp.Size-Fp.Position);
마지막으로
Fp.무료;
끝;
끝;
절차 TIbStorage.GetHead(Fp: TFileStream);
시작하다
FRptTitle := ReadAStr(Fp);
FPageHead := ReadAstr(Fp);
FPageFoot := ReadAstr(Fp);
FFieldCount := ReadAInteger(Fp);
FRecordCount := ReadAInteger(Fp);
if ReadAChar(Fp) <> '@' then showmessage('GetHead 파일 오류');
끝;
절차 TIbStorage.GetFieldNames(Fp: TFileStream);
var
Ch: 문자;
Str: 문자열;
시작하다
Str := '';
Str := ReadAStr(Fp);
FFieldNames.CommaText := Str;
Ch := ReadAChar(Fp);
if Ch <> '@' then Showmessage('필드 이름을 가져올 때 오류');
끝;
절차 TIbStorage.GetIndex(Fp: TFileStream);
var
Ch: 문자;
Str: 문자열;
시작하다
Str := '';
Str := ReadAStr(Fp);
FStreamIndex.CommaText := Str;
Ch := ReadAChar(Fp);
if Ch <> '@' then Showmessage('필드 위치 인덱스 오류를 가져올 때');
끝;
//---------필드 값 부분 읽기
함수 TIbStorage.GetFieldValue(ARecordNo, FieldNo: Integer): 문자열;
var
ID, T : 문자열;
양수: 정수;
렌, I : 정수;
Er: 부울;
시작하다
결과 := '';
어 := 거짓;
ARecordNo > FRecordCount인 경우
Er := true; //ARecordNo := FRecordCount;
ARecordNo < 1이면
Er := True; // ARecordNo := 1;
FieldNo >= FFieldCount이면
Er := True; // FieldNo := FFieldCount - 1;
ifFieldNo < 0이면
어 := 참; //필드번호 := 0;
그렇다면 어?
시작하다
Showmessage('레코드 번호 또는 필드 레이블이 범위를 벗어났습니다.');
출구;
끝;
FFieldCount = 0이면 종료합니다.
Id := Inttostr(ARecordNO) + '_' + IntToStr(FieldNo);
Pos := StrToInt(FStreamIndex.Values[Id]);
FStream.Position := 위치;
//필드 내용의 길이를 가져옵니다.
Len := ReadAInteger(FStream);
Len > 0이면
결과 := ReadBStr(FStream, Len);
만약 ReadAChar(FStream) <> '@'이면
Showmessage('필드를 읽을 때 저장 형식 찾기 오류');
끝;
절차 TIbStorage.FieldStream(ARecordNo, FieldNo: 정수; var AStream: TStream);
var
ID, T : 문자열;
양수: 정수;
렌, I : 정수;
Er: 부울;
시작하다
어 := 거짓;
ARecordNo > FRecordCount인 경우
Er := true; //ARecordNo := FRecordCount;
ARecordNo < 1이면
Er := True; // ARecordNo := 1;
FieldNo >= FFieldCount이면
Er := True; // FieldNo := FFieldCount - 1;
ifFieldNo < 0이면
어 := 참; //필드번호 := 0;
그렇다면 어?
시작하다
TDsException.Create('GetFieldValue 함수 인덱스 첨자가 범위를 벗어났습니다.');
출구;
끝;
FFieldCount = 0이면 종료합니다.
Id := Inttostr(ARecordNO) + IntToStr(FieldNo);
Pos := StrToInt(FStreamIndex.Values[Id]);
FStream.Position := 위치;
Len := ReadAInteger(FStream);
AStream.CopyFrom(FStream, Len);
끝;
function TIbStorage.GetFieldName(AIndex: Integer): string //필드 이름 가져오기;
시작하다
//저장된 필드와 데이터 유형이 각각 절반을 차지합니다.
if ((AIndex < 0) 또는 (AIndex >= FFieldNames.Count div 2)) then
Application.MessageBox('필드 이름 인덱스가 범위를 벗어났습니다.', '프로그램 오류',
Mb_Ok + Mb_IconError)
또 다른
결과 := FFieldNames.Names[AIindex*2];
끝;
function TIbStorage.GetFieldDataType(AIndex: Integer): TFieldType 가져오기;
시작하다
//저장된 필드와 데이터 유형이 각각 절반을 차지합니다.
if ((AIndex < 0) 또는 (AIndex >= FFieldNames.Count div 2)) then
Application.MessageBox('필드 데이터 유형 인덱스가 범위를 벗어났습니다.', '프로그램 오류',
Mb_Ok + Mb_IconError)
또 다른
결과 := TFieldType(StrToInt(FFieldNames.Values[FFieldNames.Names[AIindex*2+1]]));
끝;
function TIbStorage.GetDisplayLabel(AIndex: Integer): string //필드 표시 이름 가져오기;
시작하다
if ((AIndex < 0) 또는 (AIndex >= FFieldNames.Count)) then
Application.MessageBox('필드 이름 인덱스가 범위를 벗어났습니다.', '프로그램 오류',
Mb_Ok + Mb_IconError)
또 다른
결과 := FFieldNames.Values[GetFieldName(AIindex)];
끝;
끝.
테스트를 통해 이 컨트롤은 Ttable, Tquery, TaodTable, TadoQuery, TibTable, TibQuery 등과 같이 일반적으로 사용되는 데이터 세트 컨트롤을 잘 지원하며 효율성도 좋습니다(테스트: 인사 기록 1100개, 파일로 저장되는 23개 필드 약 2시간 소요) 초).
4. 컨트롤의 기본 사용법
1. 데이터 세트의 데이터를 파일에 저장
IbStorage1.Open; //스토리지 스트림 생성
IbStorage1.SaveToFile(AdataSet, Afilename);
2. 파일에서 데이터 정보 읽기
IbStorage1.열기;
IbStorage1.LoadFromFile(AfileName);
3. 데이터그램 저장 제어의 데이터에 대한 액세스
Value := IbStorage1.Fields[ArecNo, AfieldNo] //문자열 유형
그 외는 생략합니다.
5. 결론
이 데이터그램 저장 제어를 작성함으로써 데이터베이스 프로그램의 데이터 저장 및 교환 문제가 더 잘 해결되고 데이터베이스 프로그램 개발을 위한 실질적인 제어가 제공됩니다.
컨트롤은 Windows98 및 Delphi5 개발 환경에서 디버깅을 통과했습니다.