For some reason my OpenID account no longer exists even when I used it yesterday. But anyway.
I need to save record data into a .dat file. I tried a lot of searching, but it was all related to databases and BLOB things. I wasn't able to construct anything from it.
I have the following record
type
Scores = record
name: string[50];
score: integer;
end;
var rank: array[1..3] of scores;
I just need a simple way of saving and reading the record data from a .dat file. I had the book on how to do it, but that's at school.
You should also take a look at the file of-method.
This is kinda out-dated, but it's a nice way to learn how to work with files.
Since records with dynamic arrays (including ordinary strings) can't be stored to files with this method, unicode strings will not be supported. But string[50] is based on ShortStrings and your record is therefore already non-unicode...
Write to file
var
i: Integer;
myFile: File of TScores;
begin
AssignFile(myFile,'Rank.dat');
Rewrite(myFile);
try
for i := 1 to 3 do
Write(myFile, Rank[i]);
finally
CloseFile(myFile);
end;
end;
Read from file
var
i: Integer;
Scores: TScores;
myFile: File of TScores;
begin
AssignFile(myFile, 'Rank.dat');
Reset(myFile);
try
i := 1;
while not EOF(myFile) do
begin
Read(myFile, Scores);
Rank[i] := Scores; //You will get an error if i is out of the array bounds. I.e. more than 3
Inc(i);
end;
finally
CloseFile(myFile);
end;
end;
Use streams. Here is a simple demo (just demo - in practice there is no need to reopen file stream every time):
type
Scores = record
name: string[50];
score: integer;
end;
var rank: array[1..3] of scores;
procedure WriteScores(var Buf; Count: Integer);
var
Stream: TStream;
begin
Stream:= TFileStream.Create('test.dat', fmCreate);
try
Stream.WriteBuffer(Buf, SizeOf(Scores) * Count);
finally
Stream.Free;
end;
end;
procedure ReadScore(var Buf; Index: Integer);
var
Stream: TStream;
begin
Stream:= TFileStream.Create('test.dat', fmOpenRead or fmShareDenyWrite);
try
Stream.Position:= Index * SizeOf(Scores);
Stream.ReadBuffer(Buf, SizeOf(Scores));
finally
Stream.Free;
end;
end;
// write rank[1..3] to test.dat
procedure TForm1.Button1Click(Sender: TObject);
begin
rank[2].name:= '123';
WriteScores(rank, Length(Rank));
end;
// read rank[2] from test.dat
procedure TForm1.Button2Click(Sender: TObject);
begin
rank[2].name:= '';
ReadScore(rank[2], 2 - Low(rank));
ShowMessage(rank[2].name);
end;
Look in the help under "blockread" and or "blockwrite". There probably will be an example
Related
I want to get info_hash of *.torrent file using Delphi.
Tried this BEncode decorder.
But it gives crazy characters when decode.
Any other working BEncode decoder in Delphi? Or anything I'm doing wrong?
This is my code:
procedure TForm.Button1Click(Sender: TObject);
var
be: TBEncoded;
fs: tfilestream;
op: string;
begin
fs := tfilestream.Create('xx.torrent', fmOpenReadWrite);
be := TBEncoded.Create(fs);
be.Encode(be.ListData.Items[0].Data, op);
showmessage(op);
be.Encode(be.ListData.FindElement('info'), op);
showmessage(op);
end;
I've just tried this decoder, it's working normally. You didn't need to use Encode procedure, its purpose (as seen from name) is to encode elements back to BEncode. That's test program that shows torrent information in TMemo:
procedure ShowDecoded(be: TBEncoded; indent: string='');
var i: Integer;
begin
with form1.Memo1.Lines do
case be.Format of
befstring: Add(indent+be.StringData);
befInteger: Add(indent+IntToStr(be.IntegerData));
befList: begin
Add(indent+'list');
for i:=0 to be.ListData.Count-1 do
ShowDecoded(be.ListData.Items[i].Data as TBEncoded,indent+' ');
Add(indent+'end of list');
end;
befDictionary: begin
Add(indent+'dict');
for i:=0 to be.ListData.Count-1 do begin
Add(indent+' '+be.ListData.Items[i].Header+'=');
ShowDecoded(be.listData.Items[i].Data as TBEncoded,indent+' ');
end;
Add(indent+'end of dict');
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var fs: TFileStream;
be: TBEncoded;
i: Integer;
begin
if OpenDialog1.Execute then begin
fs:=TFileStream.Create(OpenDialog1.FileName,fmOpenRead);
try
be:=TBEncoded.Create(fs);
ShowDecoded(be);
be.Free;
finally
fs.Free;
end;
end;
end;
That's test result:
dict
created by=
uTorrent/3.4.3
creation date=
1439626950
encoding=
UTF-8
info=
dict
length=
1345178
name=
Алябьев А., Лист Ф. - Соловей - 1987.pdf
piece length=
16384
pieces=
)Lo.Î ’üXí»IÙçsáôt£ˆb›hŒˆ*Ð誺š¤/N7’`0âÓ†nË5&T€:V•Ìפ¯9¤Ý:¦J©Ï|Œ•A¥,¼R¯þ:H:X&…¢<¸º"2îV-vÀÖˆD†¨¬ß‰ƒ,ümjà?éÛoe¬r£{¨¾]•4òØžhô†›¼AØBeJÕÌ4³·Œ‹¶ËAG— f„\pa
end of dict
end of dict
I'd make some changes to BEncode unit, there is mess in there: raising empty exceptions, unsafe cast: TBEncoded(object) instead of "object as TBEncoded",
checking for nil object before object.free, which is tautology, but in general it works.
Update 1
Simple code to take one of the fields, 'pieces' and show in hex.
procedure FindAndShowHash(be: TBEncoded);
var i: Integer;
s: string;
infoChunk, piecesChunk: TBencoded;
begin
s:='';
infoChunk:=be.ListData.FindElement('info') as TBencoded;
piecesChunk:=infoChunk.ListData.FindElement('pieces') as TBencoded;
for i:=1 to Length(piecesChunk.StringData) do
s:=s+IntToHex(Byte(piecesChunk.StringData[i]),2);
form1.Memo1.Lines.Add('Hash function:');
form1.Memo1.Lines.Add(s);
end;
As you see, we access StringData char by char and cast it as Byte. I just showed it in hex, of course you can use these bytes for further processing.
Beware: you'll get LOADS of hex values, this is not MD5 hash or any other hash of WHOLE torrent, it's sequence of hash functions for each piece of data, usually blocks of 1 or 2 MB.
UPDATE 2
This unit can be used in newer versions of Delphi, all you need to do is to replace ALL string variables in it from 'string' to 'ANSIstring', just with Ctrl+R - ':string' replace to ':ANSIstring'.
UPDATE 3
OK, finally I get it. Here is procedure which computes info_hash and shows it in hex, this requires newer version of Delphi. Also, add IdGlobal and IdHashSHA to 'uses' section.
procedure makeInfoHash(be: TBEncoded);
var SHA1: TIdHashSHA1;
s: string;
infoChunk: TBencoded;
infoEncoded: ANSIString;
bytes: TIdBytes;
begin
infoChunk:=be.ListData.FindElement('info') as TBencoded;
TBencoded.Encode(infoChunk,infoEncoded);
bytes:=RawToBytes(infoEncoded[1],Length(infoEncoded));
SHA1:=TIdHashSHA1.Create;
try
s:=SHA1.HashBytesAsHex(bytes);
finally
SHA1.Free;
end;
Form1.Memo1.Lines.Add(s);
end;
It gives correct info_hash, the same which is displayed in uTorrent, like this:
7D0487D3D99D9C27A7C09CDCBB2F2A8034D4F9BF
You must replace all string to ANSIstring in BENcode.pas, as said in update 2. Enjoy!
Just wanting to see if there is a better way to do the following(there is always a better way for everything) because it does delay the application when loading due the amount of data.
I want to fill an array of records with data I have stored in csv file, I currently have it fixed length for the array but will later make it dynamic so I can add to the csv file.
type
TStarCoords = Packed record
szSystem: String[40];
fCoordX: Single;
fCoordY: Single;
fCoordZ: Single;
end;
SystemCoords: Array [0 .. 22379] of TStarCoords;
Const
SYSTEMS = 'Data\Systems.csv';
I then fill the array on the oncreate event
procedure TForm1.FormCreate(Sender: TObject);
var
szFile, sRecord: string;
Row, Index, i: Integer;
slList: TStringList;
begin
szFile := ExtractFilePath(ParamStr(0)) + SYSTEMS;
if FileExists(szFile) then
try
slList := TStringList.Create;
slList.LoadFromFile(szFile);
for Row := 0 to slList.Count - 1 do
begin
sRecord := slList[Row];
index := Pos(',', sRecord);
if index > 0 then
begin
SystemCoords[Row].szSystem := Copy(sRecord, 1, index - 1);
Delete(sRecord, 1, index);
end;
index := Pos(',', sRecord);
if index > 0 then
begin
SystemCoords[Row].fCoordX := StrToFloat(Copy(sRecord, 1, index - 1));
Delete(sRecord, 1, index);
end;
index := Pos(',', sRecord);
if index > 0 then
begin
SystemCoords[Row].fCoordY := StrToFloat(Copy(sRecord, 1, index - 1));
Delete(sRecord, 1, index);
end;
SystemCoords[Row].fCoordZ := StrToFloat(sRecord);
end;
finally
slList.Free;
end;
for i := Low(SystemCoords) to High(SystemCoords) do
begin
cbSystem.Items.Add(SystemCoords[i].szSystem);
end;
end;
As you can see I am using "Pos" function to parse the csv file and also loop the array at the end to add the Star name to a combobox, Is there a more economical way of doing this?
Any suggestions are welcomed
It doesn't look very efficient.
Allocating a fixed length global array looks poor. Use a dynamic array of length determined at runtime.
Short strings are not recommended. Don't use them in modern programming. They are legacy and don't handle Unicode.
Don't pack records. That results in misaligned data.
There seems to be far more heap allocations that are needed. Avoid Delete if you can.
Loading into a string list won't be efficient. Use a line reader based approach for speed. Delphi's built in class though is rubbish. If you want speed and effective use of memory, roll your own.
Probably the bulk of the time is spent populating the combo! Adding 22380 items to a combo will take a very long time. Don't do that. If the data set is smaller, only add as many items as there are in the data. Otherwise, use the virtual paradigm in your UI control.
Your next step though is to work out where the bottleneck is. We can only guess because we are missing so much information. We don't know if the data is static, how big it is, and so on.
Like others said, probably the majority of the time is spent populating the combo.
In my opinion, when dealing with big updates of a TStrings the BeginUpdate / EndUpdate technique proposed by the Jens Borrisholt's answer constitutes a valid approach.
As a minor issue, if your application is the only which writes and reads the data and neither machines nor humans care about the CSV format, you might consider to store the records adopting a different file format, using the BlockRead and BlockWrite functions.
type
TStarCoords = record
szSystem: string[40];
fCoordX,
fCoordY,
fCoordZ: Single;
end;
. . .
const
CFILENAME = '<your path to some file .dat>';
Reading the data:
procedure TForm1.FormCreate(Sender: TObject);
var
lstStarCoords: TList<TStarCoords>;
f: File;
starCoords: TStarCoords;
begin
lstStarCoords := TList<TStarCoords>.Create;
try
AssignFile(f, CFILENAME);
Reset(f, SizeOf(TStarCoords));
try
while not Eof(f) do begin
BlockRead(f, starCoords, 1);
lstStarCoords.Add(starCoords);
end;
finally
CloseFile(f);
end;
cbSystem.Items.BeginUpdate;
for starCoords in lstStarCoords do
cbSystem.Items.Add(starCoords.szSystem);
cbSystem.Items.EndUpdate;
finally
lstStarCoords.Free;
end;
end;
Writing the data:
procedure TForm1.WriteStarCoords;
var
lstStarCoords: TList<TStarCoords>;
f: File;
starCoords: TStarCoords;
i: Integer;
begin
lstStarCoords := TList<TStarCoords>.Create;
try
//let's insert 5k new items
for i:=1 to 5000 do begin
with starCoords do begin
szSystem := 'HYEL YE';
fCoordX := 122;
fCoordY := 12.375;
fCoordZ := 45.75;
end;
lstStarCoords.Add(starCoords);
end;
AssignFile(f, CFILENAME);
Rewrite(f, SizeOf(TStarCoords));
try
for starCoords in lstStarCoords do
BlockWrite(f, starCoords, 1);
finally
CloseFile(f);
end;
finally
lstStarCoords.Free;
end;
end;
EDIT: example using pointers to store the record information directly in the cbSystem component.
This approach is a little more "dangerous" since it allocates memory which has to be manually freed but allows to avoid the usage of a TDictionary to pair the TStarCoords.szSystem with the corresponding record.
Declare a new type which points to the TStarCoords record:
type
PStarCoords = ^TStarCoords;
Reading the data:
procedure TForm1.FormCreate(Sender: TObject);
var
lstStarCoords: TStringList;
f: File;
starCoords: PStarCoords;
begin
ClearCbSystem;
lstStarCoords := TStringList.Create(False);
{another minor enhancement:
since lstStarCoords does not own any TObject which needs to be freed
the OwnsObjects property of the TStringList can be set to False
in order to avoid some code to be execute in some method like Clear and Delete}
try
lstStarCoords.BeginUpdate;
AssignFile(f, CFILENAME);
Reset(f, SizeOf(TStarCoords));
try
while not Eof(f) do begin
New(starCoords);
BlockRead(f, starCoords^, 1);
lstStarCoords.AddObject(starCoords^.szSystem, TObject(starCoords));
end;
finally
CloseFile(f);
end;
lstStarCoords.EndUpdate;
cbSystem.Items.Assign(lstStarCoords);
finally
lstStarCoords.Free;
end;
end;
Clearing the list with cbSystem.Clear does not automatically dispose the underlying pointers which have to be manually freed. Use the ClearCbSystem procedure everytime the cbSystem list has to be cleared:
procedure TForm1.ClearCbSystem;
var
i: Integer;
begin
cbSystem.Items.BeginUpdate;
for i := cbSystem.Items.Count-1 downto 0 do
Dispose(PStarCoords(cbSystem.Items.Objects[i]));
cbSystem.Clear;
cbSystem.Items.EndUpdate;
end;
When the form is destroyed, a call to the ClearCbSystem procedure ensures the pointers are disposed before the cbSystem component is freed by the application itself:
procedure TForm1.FormDestroy(Sender: TObject);
begin
ClearCbSystem;
end;
You can use TStringlist for the parsing of the line. In the following I assume that you have you elements seperated by a comma.
Since you are putting the string representation of you records into a combobox I assunme you later on in your program needs to go the other way: Find a TStarCoords from string. Given that I woyls recoment you putting your elements in a TDictionary instread og a Array.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Generics.Collections, StdCtrls;
type
TStarCoords = packed record
szSystem: string[40];
fCoordX: Single;
fCoordY: Single;
fCoordZ: Single;
end;
const
SYSTEMS = 'Data\Systems.csv';
type
TForm1 = class(TForm)
ComboBox1: TComboBox;
procedure FormCreate(Sender: TObject);
procedure ComboBox1Change(Sender: TObject);
private
SystemCoords: TDictionary<string, TStarCoords>;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.ComboBox1Change(Sender: TObject);
var
StarCoord: TStarCoords;
begin
if not SystemCoords.TryGetValue(ComboBox1.Text, StarCoord) then
exit; //todo : Make some error handling
Caption := FloatToStr(StarCoord.fCoordX);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
Lines, Elements: TStringlist;
Line: string;
SystemCoord: TPair<string, TStarCoords>;
begin
if not FileExists(ExtractFilePath(ParamStr(0)) + SYSTEMS) then
exit; //todo: Some error handling
SystemCoords := TDictionary<string, TStarCoords > .Create;
Lines := TStringlist.Create;
Elements := TStringlist.Create;
Elements.LineBreak := ',';
try
for Line in Lines do
begin
Elements.Text := Line;
SystemCoord.Key := Elements[0];
with SystemCoord.Value do
begin
szSystem := string(Elements[0]);
fCoordX := StrToFloat(Elements[1]);
fCoordY := StrToFloat(Elements[2]);
fCoordZ := StrToFloat(Elements[3]);
end;
SystemCoords.Add(SystemCoord.Key, SystemCoord.Value);
end;
finally
Lines.Free;
Elements.Free;
end;
try
ComboBox1.Items.BeginUpdate;
for SystemCoord in SystemCoords do
ComboBox1.Items.Add(SystemCoord.Key);
finally
ComboBox1.Items.EndUpdate;
end;
end;
end.
Whats wrong with my code? I try to save and then load again an object to a blob field in a database, but get nothing back.
Records are saved, but I can't say if the data was written correctly because I cant read the data back.
Here is the object type:
TMyObject = class
Name: string;
end;
And here I try to save:
procedure TForm1.btnSaveObjectClick(Sender: TObject);
var
myObject: TmyObject;
aMemoryStream: TMemoryStream;
begin
myObject:= TMyObject.Create;
myObject.Name:=edtName.Text;
aMemoryStream:= TMemoryStream.Create;
aMemoryStream.Write(myObject, myObject.InstanceSize);
aMemoryStream.Position:=0;
with TSQLQuery.Create(nil) do
begin
DataBase:=Conn;
SQL.Text:='INSERT INTO testtable (data) VALUES (:data)';
try
ParamByName('data').LoadFromStream(aMemoryStream, ftBlob);
ExecSQL;
TX.CommitRetaining;
finally
aMemoryStream.Free;
myObject.Free;
Free;
end;
end;
end;
Trying to read the data back up again:
procedure TForm1.btnLoadObjectClick(Sender: TObject);
var
myObject: TMyObject;
BlobStream : TStream;
begin
with TSQLQuery.Create(nil) do
begin
DataBase:=Conn;
SQL.Text:='SELECT data FROM testtable';
myObject:= TmyObject.Create;
try
Open;
Last;
BlobStream:= CreateBlobStream(FieldByName('data'), bmread);
BlobStream.Position:=0;
BlobStream.Read(myObject, BlobStream.Size);
ShowMessage('Stored Name: ' +myObject.Name);
finally
myObject.Free;
Free;
end;
end;
end;
Also, should BlobStream be free'd?
The correct way for storing your objects into files, streams or blob fields is to first extend your object with additional methods for loading and saving data from your objects fields (objects internal variables) into single memory block.
You do this by saving one field after another.
If your objects are dynamically sized (containing dynamic arrays or strings) don't forget to store the size of these separately so you will know how much data belongs to them when loading your objects later on.
Also if your objects contain some other objects you also need them to have similar methods for storing and loading their data.
The implementation depends heavily on your object's class design. Here a code example for a string field:
type
TMyObject = class
public
Name: string;
procedure SaveToStream(AStream: TStream);
procedure LoadFromStream(AStream: TStream);
end;
procedure TMyObject.SaveToStream(AStream: TStream);
var
Len: Integer;
begin
Len := Length(Name);
AStream.Write(Len, SizeOf(Len));
AStream.Write(PChar(Name)^, Len);
end;
procedure TMyObject.LoadFromStream(AStream: TStream);
var
Len: Integer;
begin
AStream.Read(Len, SizeOf(Len));
SetString(Name, PChar(nil), Len);
AStream.Read(PChar(Name)^, Len);
end;
With this, it is possible to use the stream that CreateBlobStream returns and just save myObject to the blobfield:
BlobField := FieldByName('data') as TBlobField;
Stream := CreateBlobStream(BlobField, bmWrite);
myObject.SaveToStream(Stream);
..or load it from the stream:
Stream:= CreateBlobStream(FieldByName('data'), bmread);
myObject.LoadFromStream(Stream);
I am trying to implement a simple contact manager using the VirtualStringTree component. I have it set up to look like a list-view component with only three columns that will all contain text:
For the data structure, I am using svTree by Linas, which was mentioned in another Stack Overflow question.
I have declared a record like this:
type
TMainData = record
Name, Email, Password: string;
end;
In the form's OnCreate I have this:
procedure TForm1.FormCreate(Sender: TObject);
begin
MyTree := TSVTree<TMainData>.Create(False);
MyTree.VirtualTree := vst1;
end;
I am adding the data from TEdits like this:
procedure TForm1.BuildStructure;
var
svNode: TSVTreeNode<TMainData>;
Data: TMainData;
begin
MyTree.BeginUpdate;
try
Data.Name := edtname.Text;
Data.Email := edtEmail.Text;
Data.Password := edtPassword.Text;
svNode := MyTree.AddChild(nil, Data);
finally
MyTree.EndUpdate;
end;
Label1.Caption := 'Count: '+IntToStr(MyTree.TotalCount);
end;
How can I save this into a stream or a file to be loaded back? I have tried using MyTree.SaveToFile('C:/Test.dat') and MyTree.LoadFromFile('C:/Test.dat'), but when it's loaded back the tree view contains no data, only a blank row.
You need to set OnLoadNode and OnSaveNode procedures for your TSVTree and implement your logic here. You can look at Project2 in the Demos folder. E.g.:
uses
uSvHelpers;
MyTree.OnSaveNode := DoSave;
MyTree.OnLoadNode := DoLoad;
procedure TForm1.DoLoad(Sender: TSVTree<TMainData>; Node: TSVTreeNode<TMainData>; Stream: TStream);
var
obj: TMainData;
begin
//
if Assigned(Node) then
begin
//read from stream
//read in correct order
obj.Name := Stream.AsString;
obj.Email := Stream.AsString;
obj.Password := Stream.AsString;
Node.FValue := obj;
end;
end;
procedure TForm1.DoSave(Sender: TSVTree<TMainData>; Node: TSVTreeNode<TMainData>; Stream: TStream);
begin
if Assigned(Node) then
begin
//read from stream
Stream.WriteString(Node.FValue.Name);
Stream.WriteString(Node.FValue.Email);
Stream.WriteString(Node.FValue.Password);
end;
end;
After that you can just call MyTree.SaveToFile('C:/Test.dat') or MyTree.LoadFromFile('C:/Test.dat'). In my demo and this example i've used another unit (uSvHelpers) which implements TStream helper for more OO stream support. You can of course use your own way to write your data information to stream.
Looks like you need to implement the OnSaveNode and OnLoadNode events:
procedure TForm.VTLoadNode(Sender: TBaseVirtualTree;
Node: PVirtualNode; Stream: TStream);
begin
// Load Node Data record from the stream
end;
procedure TForm.VTSaveNode(Sender: TBaseVirtualTree;
Node: PVirtualNode; Stream: TStream);
begin
// Save Node Data record to the stream
end;
I'm sorry I'm not being clear...lets try again
I have a record type :
MyRecord = Record
Name: string;
Age: integer;
Height: integer;
several more fields....
and an INI file with:
[PEOPLE]
Name=Maxine
Age=30
maybe one or two other key/value pairs
All I want to do is load the record with the data from the INI file.
I have the data from the INI in a TStringList I want to be able to loop through the TStringList and assign/update only the Record Fields with key value pairs in the TStringList.
Charles
So you have an INI file with the content
[PEOPLE]
Name=Maxine
Age=30
and want to load it into a record defined by
type
TMyRecord = record
Name: string;
Age: integer;
end;
? That is very easy. Just add IniFiles to the uses clause of your unit, and then do
var
MyRecord: TMyRecord;
procedure TForm1.Button1Click(Sender: TObject);
begin
with TIniFile.Create(FileName) do
try
MyRecord.Name := ReadString('PEOPLE', 'Name', '');
MyRecord.Age := ReadInteger('PEOPLE', 'Age', 0);
finally
Free;
end;
end;
Of course, the MyRecord variable need not be a global variable. It can also be a local variable or a field in a class. But that all depends on your exact situation, naturally.
A Simple Generalisation
A slightly more interesting situation is if your INI files contains several people, like
[PERSON1]
Name=Andreas
Age=23
[PERSON2]
Name=David
Age=40
[PERSON3]
Name=Marjan
Age=49
...
and you want to load it into an array of TMyRecord records, then you can do
var
Records: array of TMyRecord;
procedure TForm4.FormCreate(Sender: TObject);
var
Sections: TStringList;
i: TIniFile;
begin
with TIniFile.Create(FileName) do
try
Sections := TStringList.Create;
try
ReadSections(Sections);
SetLength(Records, Sections.Count);
for i := 0 to Sections.Count - 1 do
begin
Records[i].Name := ReadString(Sections[i], 'Name', '');
Records[i].Age := ReadInteger(Sections[i], 'Age', 0);
end;
finally
Sections.Free;
end;
finally
Free;
end;
end;
If you have the INI section in a string list you can just use the Values[] property:
String list contents
Name=Maxine
Age=30
Code to read into record
MyRecord.Name := StringList.Values['Name']
MyRecord.Age = StrToInt(StringList.Values['Age'])
Naturally you would want to handle errors one way or another, but this the the basic idea.