sauvegarder les enregistrements contenant un membre de type chaîne dans un fichier (Delphi, Windows)
Question
J'ai un dossier qui ressemble à:
type
TNote = record
Title : string;
Note : string;
Index : integer;
end;
Simple . La raison pour laquelle j'ai choisi de définir les variables telles que la chaîne (par opposition à un tableau de caractères) est que je ne sais pas combien de temps ces cordes vont être. Ils peuvent être 1 caractère long, 200 ou 2000. Bien sûr, quand je tente de sauvegarder l'enregistrement dans un fichier de type (fichier ...) le compilateur se plaint que je dois donner une taille à la chaîne. Y at-il un moyen de surmonter cela? ou un moyen de sauvegarder les enregistrements dans un fichier typées et de maintenir encore une sorte de façon interrogeable?
S'il vous plaît ne me point pas solutions possibles , si vous connaissez la solution s'il vous plaît poster le code. Merci
La solution
Vous ne pouvez pas le faire avec un fichier tapé. Essayez quelque chose comme ça, avec un TFileStream:
type
TStreamEx = class helper for TStream
public
procedure writeString(const data: string);
function readString: string;
procedure writeInt(data: integer);
function readInt: integer;
end;
function TStreamEx.readString: string;
var
len: integer;
iString: UTF8String;
begin
self.readBuffer(len, 4);
if len > 0 then
begin
setLength(iString, len);
self.ReadBuffer(iString[1], len);
result := string(iString);
end;
end;
procedure TStreamEx.writeString(const data: string);
var
len: cardinal;
oString: UTF8String;
begin
oString := UTF8String(data);
len := length(oString);
self.WriteBuffer(len, 4);
if len > 0 then
self.WriteBuffer(oString[1], len);
end;
function TStreamEx.readInt: integer;
begin
self.readBuffer(result, 4);
end;
procedure TStreamEx.writeInt(data: integer);
begin
self.WriteBuffer(data, 4);
end;
type
TNote = record
Title : string;
Note : string;
Index : integer;
procedure Save(stream: TStream);
end;
procedure TNote.Save(stream: TStream);
var
temp: TMemoryStream;
begin
temp := TMemoryStream.Create;
try
temp.writeString(Title);
temp.writeString(Note);
temp.writeInt(Index);
temp.seek(0, soFromBeginning);
stream.writeInt(temp.size);
stream.copyFrom(temp, temp.size);
finally
temp.Free;
end;
end;
Je vais laisser la procédure de charge pour vous. idée de base même, mais il ne devrait pas avoir besoin d'un flux de température. Avec la taille de l'enregistrement devant chaque entrée, vous pouvez le lire et de savoir jusqu'où aller si vous êtes à la recherche d'un certain dossier # au lieu de lire la chose.
EDIT: Ceci a été écrit spécifiquement pour les versions de Delphi qui utilisent des chaînes Unicode. Sur les anciennes versions, vous pouvez simplifier un peu.
Autres conseils
Pourquoi ne pas écrire ceci comme XML? Voir ma session " XML pratique avec Delphi " sur la façon dont pour commencer avec cela.
Une autre possibilité serait de faire vos dossiers en cours sous forme descendante TComponent et stocker / récupérer vos données dans les fichiers DFM.
Cette entrée Stackoverflow vous montre comment faire.
- jeroen
PS: Désolé ma réponse XML était un peu dense; Je suis en fait sur la route pour les deux conférences ( BASTA! et DelphiLive! Allemagne ).
En fait ce que vous devez faire est très simple: créer un fichier XML exemple, puis lancez le Data Wizard XML Delphi liaison (disponible en Delphi depuis la version 6).
Cet assistant va générer une unité pour vous qui a les interfaces et les classes XML aux objets Delphi, et quelques fonctions d'aide pour les lire à partir du fichier, la création d'un nouvel objet, etc. Ma session (voir le premier lien ci-dessus) contient en fait la plupart des détails de ce processus.
Le lien ci-dessus est une vidéo montrant l'utilisation de l'Assistant Liaison de données XML Delphi.
Vous pouvez travailler avec deux fichiers différents, qui stocke juste les cordes d'une manière pratique, les autres magasins les dossiers avec une référence aux chaînes. De cette façon, vous aurez toujours un fichier de dossiers pour un accès facile, même si vous ne connaissez pas la taille du contenu.
(Désolé, pas de code.)
TNote = record
Title : string;
Note : string;
Index : integer;
end;
pourrait se traduire par
TNote = record
Title : string[255];
Note : string[255];
Index : integer;
end;
et utiliser Stream.writebuffer (ANodeVariable, sizeof (TNODE), mais vous avez dit que les chaînes se passent plus de 255 caractères dans ce cas, si une chaîne passe au-dessus 65535 caractères puis changer WORD à ENTIER
type
TNodeHeader=Record
TitleLen,
NoteLen: Word;
end;
(* this is for writing a TNode *)
procedure saveNodetoStream(theNode: TNode; AStream: TStream);
var
header: TNodeHeader;
pStr: PChar;
begin
...
(* writing to AStream which should be initialized before this *)
Header.TitleLen := Length(theNode.Title);
header.NodeLen := Length(theNode.Note);
AStream.WriteBuffer(Header, sizeof(TNodeHeader);
(* save strings *)
PStr := PChar(theNode.Title);
AStream.writeBuffer(PStr^, Header.TitleLen);
PStr := PChar(theNode.Note);
AStream.writebuffer(PStr^, Header.NoteLen);
(* save index *)
AStream.writebuffer(theNode.Index, sizeof(Integer));
end;
(* this is for reading a TNode *)
function readNode(AStream: TStream): TNode;
var
header: THeader
PStr: PChar;
begin
AStream.ReadBuffer(Header, sizeof(TNodeHeader);
SetLength(Result.Title, Header.TitleLen);
PStr := PChar(Result.Title);
AStream.ReadBuffer(PStr^, Header.TitleLen);
SetLength(Result.Note, Header.NoteLen);
PStr := PChar(Result.Note);
AStream.ReadBuffer(PStr^, Header.NoteLen);
AStream.ReadBuffer(REsult.Index, sizeof(Integer)(* 4 bytes *);
end;
Vous pouvez utiliser les fonctions disponible dans cet Open unité source .
Il vous permet de sérialiser tout contenu d'enregistrement en binaire, y compris les tableaux dynamiques, même à l'intérieur:
type
TNote = record
Title : string;
Note : string;
Index : integer;
end;
var
aSave: TRawByteString;
aNote, aNew: TNote;
begin
// create some content
aNote.Title := 'Title';
aNote.Note := 'Note';
aNote.Index := 10;
// serialize the content
aSave := RecordSave(aNote,TypeInfo(TNote));
// unserialize the content
RecordLoad(aNew,pointer(aSave),TypeInfo(TNote));
// check the content
assert(aNew.Title = 'Title');
assert(aNew.Note = 'Note');
assert(aNew.Index = 10);
end;