Lazarus

Programming => General => Topic started by: Mark12345 on February 25, 2021, 09:18:27 am

Title: Delphi 7 handling files
Post by: Mark12345 on February 25, 2021, 09:18:27 am
Hi guys i'm new with handling files and i need to know how to write read and , seek an especific position of an index array of string , the array  contains 30 slots (all of this with Tfilestream)
 

Procedure write
For i:=     to Do
Writebuffer(write the whole array)

End;

Function


Seek(position of the array lets say quest[5])
Read (quest[5])
End;

Title: Re: Delphi 7 handling files
Post by: Mark12345 on February 25, 2021, 09:31:51 am
Readbuffer not read
Title: Re: Delphi 7 handling files
Post by: lucamar on February 25, 2021, 09:48:40 am
Do you need to be able to read/write the file as a text file (let's say, something editable with Notepad)?

Not that it matters much, but you (and we) need to know that.

The process is relatively straigthforward: prepare your stream and, to write, just traverse the array and write each string to it. To read just one string, prepare the stream (if not already done) or seek to the start and read each string until you reach the one you want.

The page File Handling in Pascal (https://wiki.freepascal.org/File_Handling_In_Pascal) in the wiki can get you started on how to do it though, to be fair, it's not well thought out for beginners ...
Title: Re: Delphi 7 handling files
Post by: Mark12345 on February 25, 2021, 10:43:42 am
Yes in a .txt File

Here it's something what i did

Code: Pascal  [Select][+][-]
  1. Type
  2. Tarr=array[1..4] of string;
  3. Var
  4. Arraystring:Tarr
  5.  
  6. procedure tform1.WriteToFile4(var Arraystring:Tarr);
  7. var
  8.   fs: TFileStream;
  9.  
  10.   Len1, c, i: Cardinal;
  11. begin
  12.   Memo1.lines.clear;
  13.   SetLength(arrayString, 4);
  14.   arrayString[0] := 'First string in this Array';
  15.   arrayString[1] := 'the Second Array string';
  16.   arrayString[2] := 'String number three of this Array';
  17.   arrayString[3] := 'this is the fourth String';
  18.   fs := TFileStream.Create('C:\Users\Iña\Documents\Delphi\Streamtest.txt',
  19.                  fmCreate or fmOpenWrite or fmShareDenyWrite);
  20.   try
  21.     c := Length(arrayString);
  22.     Fs.WriteBuffer(c, SizeOf(c));
  23.     for i := 0 to c-1 do begin
  24.       Len1 := Length(arrayString[i]);
  25.       fs.WriteBuffer(Len1, SizeOf(Len1));
  26.       if Len1 > 0 then begin
  27.         fs.WriteBuffer(pointer(arrayString[i])^, Len1*sizeof(char));
  28.  
  29.       end;
  30.     end;
  31.   finally
  32.     fs.free;
  33.   end;
  34.  
  35.  
  36. procedure tform1.ReadfromFile4(var Arraystring:Tarr);
  37.   var
  38.   fs: TFileStream;
  39.  
  40.   i, Len1 : Cardinal;
  41. //  s : string;
  42. begin
  43.   fs := TFileStream.Create('C:\Users\Iña\Documents\Delphi\Streamtest.txt',
  44.                  fmOpenRead or fmShareDenyWrite);
  45.   Memo1.lines.clear;
  46.  
  47.   try
  48.     fs.ReadBuffer(Len1, SizeOf(Len1));
  49.     SetLength(arrayString, Len1);
  50.     FOR i := 0 to Len1-1 do begin
  51.       fs.ReadBuffer(Len1, SizeOf(Len1));
  52.       SetLength(arrayString[i], Len1);
  53.       Fs.ReadBuffer(pointer(arrayString[i])^, Len1*sizeof(char));
  54.       memo1.lines.add (arrayString[i]);
  55.     end;
  56.   finally
  57.     fs.free;
  58.   end;
  59. end;
  60.  

Sthe code doesn't work(here im trying to write and read all but i want to read only one position) and also i don't how to use the seek, so the thing here are two things fix the code and use the seek to read only one position of the array(the information that contains the position)
Title: Re: Delphi 7 handling files
Post by: lucamar on February 25, 2021, 10:48:53 am
You're trying to read/write the whole array at a time; instead you should be reading/writing it a string at a time.*

Allow me a little time and I'll write a small demo based on your code.


* Disregard that; the forum software parsed some of your code as BBCode. You should read here on how to post code in the forum: Forum: Use code tags (https://wiki.freepascal.org/Forum#Use_code_tags)
Title: Re: Delphi 7 handling files
Post by: Mark12345 on February 25, 2021, 10:55:57 am
I modified my comment of the code but still doesn't work(the code in delphi)
Title: Re: Delphi 7 handling files
Post by: lucamar on February 25, 2021, 11:04:36 am
Is there any reason why you want to use a TFileStream or any other solution would suffice?

Also, you're storing (or trying to) the length of each string along with the string itself; that can be more easily done with WriteAnsiString() and ReadAnsiString(). But then your file will not be a plain-text file. Is that really what you want?
Title: Re: Delphi 7 handling files
Post by: Mark12345 on February 25, 2021, 11:49:23 am
I want to use tfilestream because i'am making a quizgame and i want to write read and , seek a random question , the array contains 30 slots which has a lot of data( one question in each slot) , the code is an example of what i was trying to do
It doesn' t matter if it's .txt .dat or whatever
Tfilestream with writebuffer readbuffer is faster and more efficient than blockread blockwrite for a lot of data.
Title: Re: Delphi 7 handling files
Post by: lucamar on February 25, 2021, 12:02:39 pm
OK, here is the promised example code ...

Let's say this is the test code you want working:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. const
  3.   filename = 'C:\Users\Joe\Documents\Delphi\Streamtest.tst';
  4. var
  5.   TheStrings: TStringArray;
  6. begin
  7.   {Set string array}
  8.   SetLength(TheStrings, 4);
  9.   TheStrings[0] := 'First string in this Array';
  10.   TheStrings[1] := 'the Second Array string';
  11.   TheStrings[2] := 'String number three of this Array';
  12.   TheStrings[3] := 'this is the fourth String';
  13.   {Write it}
  14.   WriteToFile(Filename, TheStrings);
  15.   { Reset the array}
  16.   SetLength(TheStrings, 0);
  17.   {Read it back and show it}
  18.   TheStrings := ReadfromFile(filename);
  19.   if Length(TheStrings) > 0  then begin
  20.     Memo1.Lines.Clear;
  21.     Memo1.Lines.AddStrings(TheStrings);
  22.   end;
  23.   {Now read and show only line 2}
  24.   Memo1.Lines.Add('Reading line no. 2: ');
  25.   Memo1.Lines.Add(ReadfromFile(filename, 2));
  26. end;

WriteToFile() would be something like:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.WriteToFile(const Filename: String; Strings: TStringArray);
  2. var
  3.   fs: TFileStream;
  4.   c: Cardinal;
  5.   sv: String;
  6. begin
  7.   try
  8.     if FileExists(Filename) then
  9.       fs := TFileStream.Create(Filename, fmOpenWrite or fmShareDenyWrite)
  10.     else
  11.       fs := TFileStream.Create(Filename, fmCreate);
  12.     {Write the strings to the file}
  13.     try
  14.       c := Length(Strings);
  15.       fs.WriteDWord(c);
  16.       for sv in Strings do
  17.         fs.WriteAnsiString(sv);
  18.     finally
  19.       fs.free;
  20.     end;
  21.   except
  22.     on EFOpenError do
  23.       ShowMessage('Unable to create or write to:' + LineEnding + Filename);
  24.   end;
  25. end;

And the two "versions" (overloads) of ReadfromFile() would be:
Code: Pascal  [Select][+][-]
  1. function TForm1.ReadfromFile(const Filename: String): TStringArray;
  2. {This overload reads and returns the whole array.
  3.  NOTE: The caller is responsible of the array's destruction, if needed}
  4. var
  5.   fs: TFileStream;
  6.   c, i: Cardinal;
  7. begin
  8.   Result := Default(TStringArray);
  9.   if not FileExists(Filename) then
  10.     ShowMessage('Unable to read from'+ LineEnding + Filename)
  11.   else begin
  12.     fs := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
  13.     try
  14.       c := fs.ReadDWord;
  15.       SetLength(Result, c);
  16.       for i := 0 to c-1 do
  17.         Result[i] := fs.ReadAnsiString;
  18.     finally
  19.       fs.free;
  20.     end;
  21.   end;
  22. end;
  23.  
  24. function TForm1.ReadfromFile(const Filename: String; Index: Integer): String;
  25. {This overload reads and returns string no. Index (0-based)}
  26. var
  27.   fs: TFileStream;
  28.   c, i: Cardinal;
  29.   sv: String;
  30. begin
  31.   Result := '';
  32.   if not FileExists(Filename) then
  33.     ShowMessage('Unable to read from'+ LineEnding + Filename)
  34.   else begin
  35.     fs := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
  36.     try
  37.       c := fs.ReadDWord;
  38.       if c <= Index then
  39.         ShowMessageFmt(
  40.             'Requested line %d but only %d lines in file', [Index, c])
  41.       else begin
  42.         {Brute-force approach, but we are dealing with strings ...}
  43.         for i := 0 to Index do
  44.           sv := fs.ReadAnsiString;
  45.         Result := sv;
  46.       end;
  47.     finally
  48.       fs.free;
  49.     end;
  50.   end;
  51. end;

Note that since we are dealing with variable length strings you can't really use seek without complicating the code quite a little, though it can (and should) be done if there are lots of strings (on the tens of thousands or more); you have only about 30 so no problem with the brute-force approach ;)

Also, this is just an example so I've left out most of the error-checking: you should add it where appropiate for real production.

One last thing: I've only tested that it compiles cleanly, not whether it works (though it should).

ETA: For just 30 questions, even if they are long, what I would do would be to use a TStringList; once read from the file it's simplicity itself to access any line you want, and reading/writing it from/to a file is just a one-liner.
Title: Re: Delphi 7 handling files
Post by: Handoko on February 25, 2021, 12:06:32 pm
@Iña123

Have you consider using TextFile? I don't mean it is better but it is really easier than using TFileStream. If you're interested to see how to use TextFile, try these demos:

https://forum.lazarus.freepascal.org/index.php/topic,37766.msg254800.html#msg254800 (https://forum.lazarus.freepascal.org/index.php/topic,37766.msg254800.html#msg254800)
https://forum.lazarus.freepascal.org/index.php/topic,39769.msg274036.html#msg274036 (https://forum.lazarus.freepascal.org/index.php/topic,39769.msg274036.html#msg274036)

Tfilestream with writebuffer readbuffer is faster and more efficient than blockread blockwrite for a lot of data.

For the case of a lot of data, you should consider using a database or at least TDBF.
Title: Re: Delphi 7 handling files
Post by: winni on February 25, 2021, 05:06:26 pm
Hi!

If you dont like a stream and a DB seems to be too big for your needs, then there is a weird solution:

Code: Pascal  [Select][+][-]
  1. type string80 = string[80];
  2.  
  3. var F : file of string80;
  4.  
Now you can do a random acces like in any other file:
Code: Pascal  [Select][+][-]
  1. var s : string;
  2.  
  3. ....
  4. assignFile (F, './MyText.txt');
  5. reset (F);
  6. seek (F,3);
  7. read(F,s);
  8. ShowMessage ('String #3  is ' +s);
  9. closeFile(F);
  10. .....
  11.  

Take care: Never try to edit a file like this with an editor - you will crash the structure. Read only is allowed.

Writing to this kind of file is only allowed through the above shown structure.

Not such a marvelous solution.

What amount of strings are we talking about?
You could use (or misstreat) the IniFiles unit and store the strings in an IniFile.

Winni
Title: Re: Delphi 7 handling files
Post by: Mark12345 on February 25, 2021, 06:59:34 pm
Yeah, i think i should use tstringlist if you guys say that seek with tfilestream is dificult
But in the net there is less information about tstringlist than tfilestream
I don't know i'm screwed

There isn't nothing similar to write the whole unfixed array of string (array[1..30] of string) , seek a slot lets say i seek (question[5]) and then read it


Title: Re: Delphi 7 handling files
Post by: Handoko on February 25, 2021, 07:09:57 pm
... seek a slot lets say i seek (question[5]) and then read it

It seems you didn't try the demo I gave you. It really allows you to seek any line you want.

And what about Lucamar's code? Have you tried it? It should work. To me, TStringList is easier than TFileStream.
Title: Re: Delphi 7 handling files
Post by: lucamar on February 25, 2021, 07:16:05 pm
With a TStringList you would do:

to write the file,
Code: Pascal  [Select][+][-]
  1. AStrList.SaveToFile('some/file/name');

to read the file:
Code: Pascal  [Select][+][-]
  1. AStrList.LoadFromFile('some/file/name')

to add strings to the list:
Code: Pascal  [Select][+][-]
  1. AStrList.Add('Some string');
  2. {or, to put it at some position,}
  3. AStrList.Insert(Index, 'SomeString');

finally, to retrieve some string,
Code: Pascal  [Select][+][-]
  1. SomeString := AStrList[index];

There is really little to write about TStringList other than the obvious and that is fairly well covered by the documentation (https://freepascal.org/docs-html/current/rtl/classes/tstringlist.html). It can be thought of (very, very basically) as an array of strings with quite a few extra methods to handle the most common operations one would want.
Title: Re: Delphi 7 handling files
Post by: Mark12345 on February 25, 2021, 07:58:56 pm
maybe the code wasn´t working because it´s an unfixed array
(the write and read you posted)
(i put the same information in  the slots of the  array so i don't have to waste time writing something diferent)
 
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7.   Dialogs, StdCtrls;
  8.  
  9. type
  10.   TForm1 = class(TForm)
  11.     Button1: TButton;
  12.     procedure Button1Click(Sender: TObject);
  13.   private
  14.     { Private declarations }
  15.   public
  16.     { Public declarations }
  17.   end;
  18.  
  19. var
  20.   Form1: TForm1;
  21.  
  22. implementation
  23. const
  24. n=30;
  25.  
  26. type
  27. Tstringarray= array[1..n] of string;
  28. var
  29. TheStrings: TStringArray;
  30.  
  31. {$R *.dfm}
  32.  
  33.  
  34.  
  35. procedure    load(var TheStrings: TStringArray);
  36. var
  37. i:integer;
  38. begin
  39.  
  40.   TheStrings[1] := 'First string in this Array';
  41.   TheStrings[2] := 'the Second Array string';
  42.   TheStrings[3] := 'String number three of this Array';
  43.   TheStrings[4] := 'this is the fourth String';
  44.   TheStrings[5] := 'First string in this Array';
  45.   TheStrings[6] := 'the Second Array string';
  46.   TheStrings[7] := 'String number three of this Array';
  47.   TheStrings[8] := 'this is the fourth String';
  48.   TheStrings[9] := 'First string in this Array';
  49.   TheStrings[10] := 'the Second Array string';
  50.   TheStrings[11] := 'String number three of this Array';
  51.   TheStrings[12] := 'this is the fourth String';
  52.   TheStrings[13] := 'First string in this Array';
  53.   TheStrings[14] := 'the Second Array string';
  54.   TheStrings[15] := 'String number three of this Array';
  55.   TheStrings[16] := 'this is the fourth String';
  56.   TheStrings[17] := 'First string in this Array';
  57.   TheStrings[18] := 'the Second Array string';
  58.   TheStrings[19] := 'String number three of this Array';
  59.   TheStrings[20] := 'this is the fourth String';
  60.   TheStrings[21] := 'the Second Array string';
  61.   TheStrings[22] := 'String number three of this Array';
  62.   TheStrings[23] := 'this is the fourth String';
  63.   TheStrings[24] := 'First string in this Array';
  64.   TheStrings[25] := 'the Second Array string';
  65.   TheStrings[26] := 'String number three of this Array';
  66.   TheStrings[27] := 'this is the fourth String';
  67.   TheStrings[28] := 'First string in this Array';
  68.   TheStrings[29] := 'the Second Array string';
  69.   TheStrings[30] := 'String number three of this Array';
  70.  
  71.   for i:=1 to n do
  72.    begin
  73.    showmessage(TheStrings[i]);
  74.    end;
  75. end;
  76.  
  77.  
  78.  
  79.  
  80.  
  81.  
  82. procedure TForm1.Button1Click(Sender: TObject);
  83. var
  84. TheStrings: TStringArray;
  85. begin
  86. load(TheStrings);
  87. end;
  88.  
  89. end.
  90.  
  91.  
Title: Re: Delphi 7 handling files
Post by: lucamar on February 25, 2021, 08:25:22 pm
maybe the code wasn´t working because it´s an unfixed array
(the write and read you posted)

The code posted by who? My code? It does work, I tested it a little later. If it doesn't work for you, tell us what it is not doing which it should and/or what it is doing that it shouldn't, with as much detail as you can.

And, what do you mean by "unfixed", a dynamic array? It shouldn't matter; except for the need to set its length before accessing its elements (and other, more esoteric things which don't matter here) it works exactly like a normal static array.

The only difference between your code and the same but using a dynamic array is the highlighted line:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. { skipped declarations here }
  6.  
  7. implementation
  8.  
  9. const
  10.   n=30;
  11.  
  12. var
  13.   TheStrings: TStringArray;
  14.  
  15. {$R *.dfm}
  16.  
  17. procedure load(var TheStrings: TStringArray);
  18. var
  19.   i:integer;
  20. begin
  21.   {it starts from zero, but you want it from 1,
  22.    so we add one for it to go from 0 to 30 and
  23.    avoid using TheStrings[0]}
  24.   SetLength(TheStrings, n+1);
  25.   TheStrings[1] := 'First string in this Array';
  26.   {... etc ...}
  27.   TheStrings[30] := 'String number three of this Array';
  28.  
  29.   for i:=1 to n do begin
  30.     showmessage(TheStrings[i]);
  31.   end;
  32. end;
Title: Re: Delphi 7 handling files
Post by: Mark12345 on February 25, 2021, 11:19:56 pm
Thx I'm gonna try it later(the one you posted with Tfilestream)  so if all of this works the only thing that is missing is the seek
Title: Re: Delphi 7 handling files
Post by: Mark12345 on March 03, 2021, 09:20:24 pm
Guys i think i found the solution here, the only thing that remains is the seek , i had to use writebuffer and readbuffer because writeDword and readansistring didn't work in delphi 7
https://stackoverflow.com/questions/46859196/delphi-tfilestream-readbuffer-fails-to-read-string-value-from-file(the guy had an error and people helped him to correct the error, it's in the comments above the solution)
I didn't tried yet but i saw the solution of the person who helped him, (the person who helped him)she put something about char, does that char limit my array to only 255 characters, because i have more than 255 in the array )
Title: Re: Delphi 7 handling files
Post by: lucamar on March 03, 2021, 10:19:10 pm
[...] she put something about char, does that char limit my array to only 255 characters, because i have more than 255 in the array )

No, she's using SizeOf(Char) because Delphi's char type can be one or more bytes, depending on whether you're using by default ANSI or Unicode strings.
Title: Re: Delphi 7 handling files
Post by: Thaddy on March 03, 2021, 11:50:04 pm
No, she's using SizeOf(Char) because Delphi's char type can be one or more bytes, depending on whether you're using by default ANSI or Unicode strings.
Not in Delphi 7: that is strictly char...which is byte sized.. I suggest to replace any conflicting code with byte...
D7 can work with unicodestrings, but not like modern Delphi. The question is about D7...
Title: Re: Delphi 7 handling files
Post by: Mark12345 on March 04, 2021, 05:18:53 am
I tried it what they posted with ansistring and Dword and it doesn't start(a lot of errors), because when you put fs. Then it displays a lot of things like write read writebuffer seek readbuffer but it doesn't display ansistring and dword so i think i'm gonna have to use this(the link i posted), so right now the only thing that i don't know how to use is the seek(can you guys help me ?),
 And also Where it says char i have to replace it with byte?
Title: Re: Delphi 7 handling files
Post by: Mark12345 on March 08, 2021, 09:38:07 pm
Guys i replaced the sizeofchar with sizeofbyte and works aswell so the thing that is left is the seek, here it's and example of what i want
https://stackoverflow.com/questions/45548492/delphi-read-file-at-specific-address  (a guy above give him the solution ) so he seek the position sofrombegining and the number he wants to read, the only thing is i don't know if every offset of the array is 1 byte, i think if i put seek($23, sofrombegining) and then fs.readbuffer(TheStrings,1) like he did it would read the whole content of the TheString[23]
Title: Re: Delphi 7 handling files
Post by: Mark12345 on April 01, 2021, 11:17:24 pm
i'm trying to seek and read a file, i think this is what i want
https://stackoverflow.com/questions/45548492/delphi-read-file-at-specific-address
but i don't know what is the program doing because he's reading bytes at a certain offset, i have an array from 1 to 10 of string and i declared  tLength(TheStrings, n+1); ,i want to read all the info that has inside that position let's say  i want to read a random position of thestring[position], random1:=random(10)+1; first i will use seek (random1 minus 1 because it's starts from 0 reading right?sofrombegining or it will starts in 1 because i declared the tlenght from 1 to 10), And then i don't know what to do because he's reading bytes and i don't understand that, i have to know how many bytes has the position that i'm gonna read or what? let's say the random1 got the number 5 so it will have to read the position 4 or the 5 if it starts from 1,
Also i don't know why he put $ in front of the number
And then show it in a memo1(i don't know how)
Title: Re: Delphi 7 handling files
Post by: Mark12345 on April 02, 2021, 06:23:13 am
When i say read a specific position i mean read an specific line
TinyPortal © 2005-2018