1. نظرة عامة
عند كتابة برامج قواعد البيانات في دلفي، غالبًا ما تتضمن عمليات استيراد وتصدير البيانات، مثل: تخزين البيانات في قاعدة بيانات كبيرة كملفات محمولة لسهولة قراءتها في الخارج؛ قاعدة البيانات كملفات بيانات، فمن الأسهل تبادل البيانات داخل وبين البرامج، وتجنب الخطوات المرهقة لتبادل البيانات من خلال الذاكرة. على سبيل المثال، في برنامج التقرير العام الذي كتبه المؤلف، يتم استخدام عنصر التحكم هذا كحامل معلومات البيانات توصيل.
2. الأفكار الأساسية
كعنصر تحكم في تخزين مخطط البيانات، يجب أن يكون قادرًا على تخزين وقراءة المعلومات الأساسية لمجموعة البيانات (مثل: اسم الحقل، اسم عرض الحقل، نوع بيانات الحقل، عدد السجلات، عدد الحقول، القيمة الحالية للحقل المحدد في السجل المحدد، وما إلى ذلك)، وينبغي أن توفر خصائص تعبئة أفضل لسهولة الاستخدام.
وبناءً على ذلك، استخدم المؤلف الخصائص الشيئية لـ Delphi5.0 لتصميم وتطوير التحكم في تخزين مخطط البيانات.
3. طريقة التنفيذ
اكتب وحدة الكود التالية:
وحدة IbDbFile؛
واجهة
يستخدم Windows وSysUtils وClasses وForms وDb وDbTables وDialogs؛
ثابت
علامة = 'Datagram-Jixing Software Studio';
يكتب
TDsException = Class(Exception);
TIbStorage = فئة (TComponent)
خاص
FRptTitle: string; // وصف مخطط بيانات التخزين
FPageHead: سلسلة؛ // وصف رأس الصفحة
FPageFoot: سلسلة؛ // وصف القدم
FFieldNames: TStrings؛ // جدول اسم الحقل
FStreamIndex: TStrings؛
FStream: TStream; // الدفق الذي يخزن محتوى الحقل
FFieldCount: عدد صحيح؛
FRecordCount: عدد صحيح؛
FOpenFlag: Boolean؛ // ما إذا كان الدفق قد تم إنشاؤه أم لا
محمي
إعادة تعيين الإجراء؛ // إعادة تعيين --- مسح محتويات الدفق
الإجراء SaveHead(ADataSet: TDataSet; Fp: TStream); // معلومات رأس تقرير التخزين
الإجراء LoadTableToStream(ADataSet: TDataSet); // بيانات سجل التخزين
الإجراء IndexFields(ADataSet: TDataSet); // احفظ أسماء الحقول لمجموعة البيانات في القائمة
الإجراء GetHead(Fp: TFileStream); //حفظ معلومات رأس التقرير
إجراء GetIndex(Fp: TFileStream); // إنشاء مؤشر دفق السجل
الإجراء GetFieldNames(Fp: TFileStream); // اقرأ جدول اسم الحقل من الدفق
دالة GetFieldName(AIindex: Integer): string; // احصل على اسم الحقل
وظيفة GetFieldDataType(AIndex: Integer): TFieldType;
وظيفة GetDisplayLabel(AIindex: Integer): سلسلة // احصل على اسم عرض الحقل
الإجراء SaveFieldToStream(AStream: TStream; AField: TField); // احفظ الحقل في الدفق
دالة GetFieldValue(ARecordNo, FieldNo: Integer): string;
عام
إنشاء المنشئ (AOwner: TComponent)؛
التدمير المدمر؛
إجراء مفتوح // إنشاء دفق للتحضير لتخزين البيانات
الإجراء SaveToFile(ADataSet: TDataSet; AFileName: string); // طريقة التخزين
الإجراء LoadFromFile(AFileName: string);
الإجراء FieldStream(ARecordNo, FieldNo: Integer; var AStream: TStream);
خاصية FieldNames [الفهرس: عدد صحيح]: قراءة السلسلة GetFieldName؛ // اسم الحقل
خاصية FieldDataTypes [الفهرس: عدد صحيح]: قراءة TFieldType GetFieldDataType؛
خاصية FieldDisplayLabels [الفهرس: عدد صحيح]: قراءة السلسلة GetDisplayLabel؛
حقول الخاصية [RecNo, FieldIndex: Integer]: قراءة السلسلة GetFieldValue؛
// خاصية FieldStreams[RecNo, FieldIndex: Integer]: قراءة TStream 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: string);
إجراء WriteBStr(AStream: TStream; AStr: string);
الإجراء WriteAInteger(AStream: TStream; AInteger: Integer);
سجل الإجراء؛
تطبيق
سجل الإجراء؛
يبدأ
RegisterComponents('الوصول إلى البيانات', [TIbStorage]);
نهاية؛
وظيفة ReadAChar(AStream: TStream): Char;
فار
أشار: شار؛
يبدأ
ASStream.Read(AChar, 1);
النتيجة := أشار؛
نهاية؛
وظيفة ReadAStr(AStream: TStream): سلسلة؛
فار
سلسلة: سلسلة؛
ج:شار؛
يبدأ
شارع := '';
C := ReadAChar(AStream);
بينما C <> #0 يفعل
يبدأ
Str := Str + C;
C := ReadAChar(AStream);
نهاية؛
النتيجة := شارع؛
نهاية؛
وظيفة ReadBStr(AStream: TStream; الحجم: عدد صحيح): سلسلة;
فار
سلسلة: سلسلة؛
ج: شار؛
أنا: عدد صحيح؛
يبدأ
شارع := '';
لأني := 1 لحجم القيام به
يبدأ
C := ReadAChar(AStream);
Str := Str + C;
نهاية؛
النتيجة := شارع؛
نهاية؛
وظيفة ReadAInteger(AStream: TStream): عدد صحيح؛
فار
سلسلة: سلسلة؛
ج: شار؛
يبدأ
النتيجة := ماكسينت؛
شارع := '';
C := ReadAChar(AStream);
بينما C <> #0 يفعل
يبدأ
Str := Str + C;
C := ReadAChar(AStream);
نهاية؛
يحاول
النتيجة := StrToInt(Str);
يستثني
application.MessageBox("لا يمكن تحويل السلسلة الحالية إلى عدد صحيح!"، "خطأ"،
Mb_Ok + Mb_IconError);
نهاية؛
نهاية؛
الإجراء WriteAStr(AStream: TStream; AStr: string);
يبدأ
AStream.Write(Pointer(AStr)^, Length(AStr) + 1);
نهاية؛
إجراء WriteBStr(AStream: TStream; AStr: string);
يبدأ
AStream.Write(Pointer(AStr)^, Length(AStr));
نهاية؛
الإجراء WriteAInteger(AStream: TStream; AInteger: Integer);
فار
S: سلسلة؛
يبدأ
S := IntToStr(AInteger);
WriteAstr(AStream, S);
نهاية؛
المُنشئ TIbStorage.Create(AOwner: TComponent);
يبدأ
ورثت إنشاء (AOwner)؛
FOpenFlag := False; // وضع علامة لتحديد ما إذا كان قد تم إنشاء الدفق أم لا
نهاية؛
المدمر TIbStorage.Destroy;
يبدأ
إذا FOpenFlag بعد ذلك
يبدأ
FStream.Free;
fStreamIndex.Free;
FFfieldNames.Free;
نهاية؛
التدمير الموروث؛
نهاية؛
الإجراء TIbStorage.Open؛
يبدأ
FOpenFlag := صحيح؛
FStream := TMemoryStream.Create;
FStreamIndex := TStringList.Create;
FFieldNames := TStringList.Create;
إعادة ضبط؛
نهاية؛
الإجراء TIbStorage.Reset؛
يبدأ
إذا FOpenFlag بعد ذلك
يبدأ
FFfieldNames.Clear;
fStreamIndex.Clear;
FStream.Size := 0;
FRptTitle := '';
FPageHead := '';
FPageFoot := '';
FFieldCount := 0;
FRecordCount := 0;
نهاية؛
نهاية؛
//-------حفظ جزء البيانات
الإجراء TIbStorage.SaveToFile(ADataSet: TDataSet; AFileName: string);
فار
Fp: TFileStream;
أنا: عدد صحيح؛
الفصل: شار؛
T1، T2: TDateTime؛
سلسلة: سلسلة؛
يبدأ
إذا لم يكن FOpenFlag ثم
يبدأ
showmessage("الكائن غير مفتوح");
مخرج؛
نهاية؛
يحاول
إذا FileExists(AFileName) ثم حذف File(AFileName);
Fp := TFileStream.Create(AFileName, fmCreate);
إعادة ضبط؛
SaveHead(ADataSet, Fp); // احفظ معلومات الرأس --- تعليمات إضافية
IndexFields(ADataSet); // احفظ المعلومات الميدانية لمجموعة البيانات في FFieldName
LoadTableToStream(ADataSet); // احفظ معلومات البيانات الخاصة بمجموعة البيانات
WriteAStr(Fp, FFieldNames.Text); // معلومات اسم حقل التخزين
الفصل := '@';
Fp.Write(الفصل، 1);
WriteAStr(Fp, FStreamIndex.Text); // قائمة فهرس حقول التخزين
الفصل := '@';
Fp.Write(الفصل، 1);
Fp.CopyFrom(FStream, 0);
أخيراً
Fp.Free;
نهاية؛
نهاية؛
الإجراء TIbStorage.SaveHead(ADataSet: TDataSet; Fp: TStream);
فار
أنا: عدد صحيح؛
الفصل: شار؛
يبدأ
إذا لم يكن ADataSet.Active ثم ADataSet.Active := صحيح؛
WriteAStr(Fp, Flag);
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));
الفصل := '@';
Fp.Write(الفصل، 1);
نهاية؛
الإجراء TIbStorage.IndexFields(ADataSet: TDataSet);
فار
أنا: عدد صحيح؛
المجال: TField؛
يبدأ
بالنسبة إلى: = 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);
فار
لا: عدد صحيح؛
I، J، الحجم: عدد صحيح؛
Tmp، معرف، سلسلة: //id=string(RecNO) + string(FieldNo)
لين: عدد صحيح؛
الفصل: شار؛
بلوب ستريم: تي بلوب ستريم؛
يبدأ
إذا لم يكن FOpenFlag ثم
يبدأ
showmessage("الكائن غير مفتوح");
مخرج؛
نهاية؛
يحاول
ADataSet.DisableControls;
ADataSet.First;
لا := 0;
fStreamIndex.Clear;
FStream.Size := 0;
في حين لا ADataSet.Eof القيام به
يبدأ
لا := لا +1؛
بالنسبة لـ J := 0 إلى ADataSet.Fields.Count - 1 do
يبدأ
المعرف := 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);
فار
الحجم: عدد صحيح؛
الفصل: شار؛
XF: تي ستريم؛
سلسلة: سلسلة؛
يبدأ
إذا AField.IsBlob ثم
يبدأ
// كيفية تخزين محتويات حقل TblobField كدفق
Xf := TblobStream.Create(TBlobField(AField), bmread);
يحاول
إذا Xf.Size > 0 ثم
يبدأ
الحجم : = Xf.Size؛
WriteAIInteger(AStream, Size);
AStream.CopyFrom(Xf, Xf.Size);
نهاية
آخر
WriteAIInteger(AStream, 0);
أخيراً
XF.Free;
نهاية؛
نهاية
آخر
يبدأ
Str := AField.AsString;
الحجم: = الطول (ستر)؛
WriteAIInteger(AStream, Size);
إذا كان الحجم <> 0 ثم
AStream.Write(Pointer(Str)^, Size);
//WriteAstr(AStream, Str);
نهاية؛
الفصل := '@';
ASStream.Write(Ch, 1);
نهاية؛
//------------تحميل البيانات
الإجراء TIbStorage.LoadFromFile(AFileName: string);
فار
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.Free;
نهاية؛
نهاية؛
الإجراء TIbStorage.GetHead(Fp: TFileStream);
يبدأ
FRptTitle := ReadAStr(Fp);
FPageHead := ReadAstr(Fp);
FPageFoot := ReadAstr(Fp);
FFieldCount := ReadAInteger(Fp);
FRecordCount := ReadAIteger(Fp);
إذا كان ReadAChar(Fp) <> '@' ثم showmessage('GetHead File Error');
نهاية؛
الإجراء TIbStorage.GetFieldNames(Fp: TFileStream);
فار
الفصل: شار؛
سلسلة: سلسلة؛
يبدأ
شارع := '';
Str := ReadAStr(Fp);
FFieldNames.CommaText := Str;
Ch := ReadAChar(Fp);
إذا كان Ch <> '@' ثم Showmessage('عند الحصول على خطأ في أسماء الحقول');
نهاية؛
الإجراء TIbStorage.GetIndex(Fp: TFileStream);
فار
الفصل: شار؛
سلسلة: سلسلة؛
يبدأ
شارع := '';
Str := ReadAStr(Fp);
FStreamIndex.CommaText := Str;
Ch := ReadAChar(Fp);
إذا كان Ch <> '@' ثم Showmessage('عند الحصول على خطأ في فهرس موضع الحقل');
نهاية؛
//---------اقرأ جزء قيمة الحقل
الدالة TIbStorage.GetFieldValue(ARecordNo, FieldNo: Integer): string;
فار
المعرف، T: سلسلة؛
نقاط البيع: عدد صحيح؛
لين، أنا : عدد صحيح؛
عير: منطقية؛
يبدأ
النتيجة := '';
إير := خطأ؛
إذا ARecordNo > FRecordCount ثم
Er := true;
إذا كان ARecordNo <1 ثم
Er := صحيح؛ // ARecordNo := 1;
إذا كان FieldNo >= FFieldCount إذن
Er := True; // FieldNo := FFieldCount - 1;
ifFieldNo <0 إذن
Er := True; //FieldNo := 0;
إذا إيه ثم
يبدأ
Showmessage("رقم السجل أو تسمية الحقل خارج الحدود");
مخرج؛
نهاية؛
إذا كان FFieldCount = 0 ثم خروج؛
المعرف := Inttostr(ARecordNO) + '_' + IntToStr(FieldNo);
Pos := StrToInt(FStreamIndex.Values[Id]);
FStream.Position := Pos;
// احصل على طول محتوى الحقل
Len := ReadAInteger(FStream);
إذا لين > 0 ثم
النتيجة := ReadBSStr(FStream, Len);
إذا كان ReadAChar(FStream) <> '@' إذن
Showmessage("عند قراءة الحقل، ابحث عن خطأ في تنسيق الحفظ");
نهاية؛
الإجراء TIbStorage.FieldStream(ARecordNo, FieldNo: Integer; var AStream: TStream);
فار
المعرف، T: سلسلة؛
نقاط البيع: عدد صحيح؛
لين، أنا : عدد صحيح؛
عير: منطقية؛
يبدأ
إيه := خطأ؛
إذا ARecordNo > FRecordCount ثم
Er := true;
إذا كان ARecordNo <1 ثم
Er := صحيح؛ // ARecordNo := 1;
إذا كان FieldNo >= FFieldCount إذن
Er := True; // FieldNo := FFieldCount - 1;
ifFieldNo <0 إذن
Er := True; //FieldNo := 0;
إذا إيه ثم
يبدأ
TDsException.Create('GetFieldValue دالة فهرس منخفض خارج الحدود');
مخرج؛
نهاية؛
إذا كان FFieldCount = 0 ثم خروج؛
المعرف := Inttostr(ARecordNO) + IntToStr(FieldNo);
Pos := StrToInt(FStreamIndex.Values[Id]);
FStream.Position := Pos;
Len := ReadAInteger(FStream);
AStream.CopyFrom(FStream, Len);
نهاية؛
وظيفة TIbStorage.GetFieldName(AIindex: Integer): سلسلة // احصل على اسم الحقل
يبدأ
// تمثل الحقول وأنواع البيانات المخزنة نصف كل منها
إذا ((AIndex < 0) أو (AIindex >= FFieldNames.Count div 2)) ثم
Application.MessageBox ("فهرس اسم الحقل خارج الحدود"، "خطأ في البرنامج"،
Mb_Ok + Mb_IconError)
آخر
النتيجة := FFieldNames.Names[AIIndex*2];
نهاية؛
وظيفة TIbStorage.GetFieldDataType(AIIndex: Integer): TFieldType; // احصل على اسم الحقل
يبدأ
// تمثل الحقول وأنواع البيانات المخزنة نصف كل منها
إذا ((AIndex < 0) أو (AIindex >= FFieldNames.Count div 2)) ثم
Application.MessageBox ("فهرس نوع بيانات الحقل خارج الحدود"، "خطأ في البرنامج"،
Mb_Ok + Mb_IconError)
آخر
النتيجة := TFieldType(StrToInt(FFieldNames.Values[FFieldNames.Names[AIIndex*2+1]]));
نهاية؛
وظيفة TIbStorage.GetDisplayLabel(AIindex: Integer): سلسلة // احصل على اسم عرض الحقل
يبدأ
إذا ((AIndex < 0) أو (AIindex >= FFieldNames.Count)) ثم
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.Open;
IbStorage1.LoadFromFile(AfileName);
3. الوصول إلى البيانات في التحكم في تخزين مخطط البيانات
القيمة := IbStorage1.Fields[ArecNo, AfieldNo]; //نوع السلسلة
تم حذف الآخرين.
5. الاستنتاج
من خلال كتابة التحكم في تخزين مخطط البيانات هذا، يتم حل مشكلة تخزين البيانات وتبادلها في برامج قواعد البيانات بشكل أفضل، ويتم توفير تحكم عملي لتطوير برامج قواعد البيانات.
اجتاز عنصر التحكم تصحيح الأخطاء ضمن بيئة تطوير Windows98 وDelphi5.