Recent

Author Topic: Gooble-dee-gok in my data file...  (Read 865 times)

OC DelGuy

  • Full Member
  • ***
  • Posts: 218
  • 123
Gooble-dee-gok in my data file...
« on: May 07, 2025, 07:37:24 am »
I'm not sure what's going on here.  I've tried to find what's wrong, but I can't see it.  Best I can figure is that writing the whole record as a TNote or writing the records into a File of TNotes is wrong somehow.

Code: Pascal  [Select][+][-]
  1. unit SimpNotes;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Interfaces, FileUtil, ExtCtrls;
  5. type
  6.   { TForm1 }
  7.   TShortString = String[255]; // Fixed-length string type of 255 characters.
  8.   TContentString = String[255]; // Fixed-length string type of 255 characters.
  9.  
  10.   TNotes = packed record
  11.     Title: TShortString;
  12.     Content: TContentString;
  13.   end;
  14.  
  15.   TForm1 = class(TForm)
  16.     BtnAdd: TButton;
  17.     BtnDelete: TButton;
  18.     BtnSave: TButton;
  19.     BtnMod: TButton;
  20.     BtnNew: TButton;
  21.     Memo1: TMemo;
  22.     Title: TEdit;
  23.     Label1: TLabel;
  24.     Label2: TLabel;
  25.     TitlesList: TListBox;
  26.     Content: TMemo;
  27.     procedure BtnAddClick(Sender: TObject);
  28.     procedure BtnDeleteClick(Sender: TObject);
  29.     procedure BtnModClick(Sender: TObject);
  30.     procedure BtnSaveClick(Sender: TObject);
  31.     procedure BtnNewClick(Sender: TObject);
  32.     procedure FormCreate(Sender: TObject);
  33.     procedure TitlesListClick(Sender: TObject);
  34.   private
  35.     Note: array of TNotes;
  36.     NoteCount: Integer;
  37.     procedure LoadNotes;
  38.     procedure SaveNotes;
  39.   public
  40.   end;
  41.  
  42.   var
  43.     Form1: TForm1;
  44.  
  45. implementation
  46. {$R *.lfm}
  47. { TForm1 }
  48.  
  49.   Procedure TForm1.BtnAddClick(Sender: TObject);
  50.     Var
  51.       NewNote: TNotes;
  52.     Begin
  53.       If Trim(Title.Text) = '' Then
  54.         Begin
  55.           ShowMessage('Please enter a title for the note.');
  56.           Exit;
  57.         End;
  58.       If Trim(Content.Text) = '' Then
  59.         Begin
  60.           ShowMessage('Please enter content for the note.');
  61.           Exit;
  62.         End;
  63.       NewNote.Title := Title.Text;
  64.       NewNote.Content := Content.Text;
  65.       Inc(NoteCount);  // Increment the Note Counter.
  66.       SetLength(Note, NoteCount);  // Add a blank space for new Note
  67.       Note[NoteCount - 1] := NewNote;  // Add the new note.
  68.       TitlesList.Items.Add(NewNote.Title);  // Add the title to the ListBox.
  69.       Label2.Caption := 'Notes: ' + IntToStr(NoteCount);  // Update the note counter display;
  70.       Title.Clear;
  71.       Content.Clear;
  72.     End;
  73.  
  74.   Procedure TForm1.BtnModClick(Sender: TObject);
  75.     Var
  76.       I: Integer;
  77.     Begin
  78.       If TitlesList.ItemIndex >= 0 Then
  79.         Begin
  80.           I := TitlesList.ItemIndex;
  81.           Note[I].Title := Title.Text;
  82.           Note[I].Content := Content.Text;
  83.           TitlesList.Items[I] := Title.Text; // Update the listbox text
  84.         End;
  85.     End;
  86.  
  87.   Procedure TForm1.BtnDeleteClick(Sender: TObject);
  88.     Var
  89.       I: Integer;
  90.     Begin
  91.       If TitlesList.ItemIndex >= 0 Then
  92.         Begin
  93.           I := TitlesList.ItemIndex;  // This is the highlighted Title in the ListBox
  94.           TitlesList.Items.Delete(I);  // This removes the Title to be deleted.
  95.           Move(Note[I + 1], Note[I], (NoteCount - I - 1) * SizeOf(TNotes));  // Move the below records up one on the list.
  96.           Dec(NoteCount);  // Decrement the Note Counter.
  97.           SetLength(Note, NoteCount);  // Set the note array records to NoteCount records.
  98.           Label2.Caption := 'Notes: ' + IntToStr(NoteCount);  // Update the note counter display;
  99.           Title.Clear;
  100.           Content.Clear;
  101.         End;
  102.     End;
  103.  
  104.   Procedure TForm1.BtnSaveClick(Sender: TObject);
  105.     Begin
  106.       SaveNotes;
  107.     End;
  108.  
  109.   Procedure TForm1.BtnNewClick(Sender: TObject);
  110.     Begin
  111.       Title.Clear;
  112.       Content.Clear;
  113.     End;  // BtnNewClick
  114.  
  115.   Procedure TForm1.FormCreate(Sender: TObject);
  116.     Begin
  117.       NoteCount := 0;  // Init the Note Count
  118.       LoadNotes;
  119.       Label2.Caption := 'Notes: ' + IntToStr(NoteCount);  // Update the note counter display;
  120.     End;  // FormCreate
  121.  
  122.   Procedure TForm1.TitlesListClick(Sender: TObject);
  123.     Begin
  124.       If TitlesList.ItemIndex >= 0 Then
  125.         Begin
  126.           Title.Text := Note[TitlesList.ItemIndex].Title;
  127.           Content.Text := Note[TitlesList.ItemIndex].Content;
  128.         End;
  129.     End;  // Proc TitlesListClick
  130.  
  131.   Procedure TForm1.LoadNotes;
  132.     Var
  133.       F: File of TNotes;
  134.       I, ErrorCode: Integer;
  135.     Begin
  136.       If FileExists('Notes.dat') Then
  137.         Begin
  138.           AssignFile(F, 'Notes.dat');
  139.           {$I-} // Disable I/O checking
  140.           Reset(F);
  141.           ErrorCode := IOResult;
  142.           Memo1.Lines.Add('Notes Opened: ' + IntToStr(ErrorCode));
  143.           {$I+} // Enable I/O checking
  144.           If ErrorCode <> 0 Then
  145.             Begin
  146.               ShowMessage('Error opening notes file: ' + SysErrorMessage(ErrorCode));
  147.               NoteCount := 0;  SetLength(Note, 0);  Exit;
  148.             End;
  149.            Try
  150.             NoteCount := {FileSize(F)}512 div SizeOf(TNotes);
  151.             Memo1.Lines.Add('FileSize: ' + IntToStr(FileSize(F)));
  152.             Memo1.Lines.Add('Size Of - TNotes: ' + IntToStr(SizeOf(TNotes)));
  153.             Memo1.Lines.Add('Note Count: ' + IntToStr(NoteCount));
  154.             SetLength(Note, NoteCount);
  155.             For I := 0 to NoteCount - 1 Do
  156.               Begin
  157.                 {$I-} // Disable I/O checking
  158.                 Read(F, Note[I]);
  159.                 ErrorCode := IOResult;
  160.                 Memo1.Lines.Add('Reading Notes: ' + IntToStr(ErrorCode));
  161.                 {$I+} // Enable I/O checking
  162.                 If ErrorCode <> 0 Then
  163.                   Begin
  164.                     ShowMessage('Error reading notes file at index: ' + IntToStr(I) + ' - ' + SysErrorMessage(ErrorCode));
  165.                     NoteCount := 0;
  166.                     SetLength(Note, 0);
  167.                     TitlesList.Clear;
  168.                     CloseFile(F);
  169.                     Exit;
  170.                   End; // ErrorCode
  171.                 TitlesList.Items.Add(Note[I].Title);
  172.               End; // For I
  173.           Finally
  174.             {$I-} // Disable I/O checking
  175.             CloseFile(F);
  176.             ErrorCode := IOResult;
  177.             {$I+} // Enable I/O checking
  178.             If ErrorCode <> 0 Then
  179.               Begin
  180.                 ShowMessage('Error closing notes file: ' + SysErrorMessage(ErrorCode));
  181.               End;
  182.           End; // Finally
  183.         End // FileExists
  184.       Else
  185.         Begin
  186.           NoteCount := 0;
  187.           SetLength(Note, 0);
  188.         End; // Else
  189.     End; // Proc LoadNotes
  190.  
  191.   Procedure TForm1.SaveNotes;
  192.     Var
  193.       F: File of TNotes;
  194.       I: Integer;
  195.     Begin
  196.       AssignFile(F, 'Notes.dat');
  197.       Rewrite(F);
  198.       For I := 0 to NoteCount - 1 Do
  199.         Begin
  200.           Write(F, Note[I]);
  201.         End;
  202.       CloseFile(F);
  203.     End;  // Proc SaveNotes
  204.  
  205. end.


Why does the SaveNotes produce this:   :o (What's all the gooble-dee-gok around my Notes?)?
Code: Text  [Select][+][-]
  1. The Lord of the Rings                 ` €                            xébÍÿ           W      O¬òÿ  c       å*\.žÉ  þÿÿÿ    0             é?    y cÍÿ  þÿÿÿ    +I­òÿ  H                              p       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒ³ôÿ  ·ôÿ          cFrodo and company set off to Mordor.  Isengard and Barad Dur.  Aragorn enters the white city.
  2.                             ðz      (    +       ¶    ÿÿÿÿ    ê?            (    T
  3.      ðz            ‘d     °KlÍÿ          T
  4.      ðz      
  5. The Hobbitòÿ                         ` €                            xébÍÿ          @ZW     O¬òÿ          [hŒ?‰         p–Š             é?    y cÍÿ         +I­òÿ  H                              p       ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒ³ôÿ  ·ôÿ          Bilbo and the 13 Dwarves go to Erebor.      °KlÍÿ  \¯ôÿ  p~    °KlÍÿ  N
  6.  
  7.      ðzM    ðz      N
  8.  
  9.                                      ðz      8à    +       F     ÿÿÿÿ    ê?            (    N
  10.  
  11.      ðzM    P!T    !h     °KlÍÿ          N
  12.  
  13.      ðz      
  14.  


And when reading the file, why does the File Size = 2?   >:D(see attachment)      Two is the number of records.
Shouldn't it be 1024?  %)
Free Pascal Lazarus Version #: 2.2.4
Date: 24 SEP 2022
FPC Version: 3.2.2
Revision: Lazarus_2_2_4
x86_64-win64-win32/win64

Fibonacci

  • Hero Member
  • *****
  • Posts: 788
  • Internal Error Hunter
Re: Gooble-dee-gok in my data file...
« Reply #1 on: May 07, 2025, 07:57:31 am »
Why does the SaveNotes produce this

Part of the string not filled with text is random garbage, and also each ShortString (your String[255] is a ShortString) contains a byte with its lengh at front (non printable character usually).



And when reading the file, why does the File Size = 2?   >:D(see attachment)

https://www.freepascal.org/docs-html/rtl/system/filesize.html

"Filesize returns the total number of records in file F"



Code: Pascal  [Select][+][-]
  1. NoteCount := {FileSize(F)}512 div SizeOf(TNotes);

Isnt the app only reading one note even though the file contains more than one?



Suggestions

1. Stop using this archaic AssignFile/Rewrite, and "File of Record" thing; instead use MemoryStream/FileStream

2. Consider using JSON as a storage format
« Last Edit: May 07, 2025, 10:35:00 am by Fibonacci »

Thaddy

  • Hero Member
  • *****
  • Posts: 18765
  • To Europe: simply sell USA bonds: dollar collapses
Re: Gooble-dee-gok in my data file...
« Reply #2 on: May 07, 2025, 09:44:01 am »
I agree with you that using assignfile etc is a bit silly in an application that is written using classes and a gui based on lazarus, but it is not archaic for non-gui applications at all. In this case I would also recommend using memory/filestreams because the overhead is already in place.
But warn that assignfile should be simply assign....
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

OC DelGuy

  • Full Member
  • ***
  • Posts: 218
  • 123
Re: Gooble-dee-gok in my data file...
« Reply #3 on: May 07, 2025, 05:18:27 pm »
And when reading the file, why does the File Size = 2?   >:D(see attachment)

https://www.freepascal.org/docs-html/rtl/system/filesize.html

"Filesize returns the total number of records in file F"
OK, this is really my mistake.  The reason this appears is because I saw it in an example on a website (See also second quote below.).

So, what is the way to get the actual file size and not the record size?

In my defense, with a name like FileSize, I didn't read the reference docs.  That is why I used it in line 150  to DIV it by the record size.  That is how I was getting the number of records.

Code: Pascal  [Select][+][-]
  1. NoteCount := {FileSize(F)}512 div SizeOf(TNotes);

Isnt the app only reading one note even though the file contains more than one?
Yes, but I was forcing the app to read one record.  I added the {}512 after my first test run.  The original line was:
Code: Pascal  [Select][+][-]
  1. NoteCount := FileSize(F) div SizeOf(TNotes);
I forgot to take that out for this posting.  When I was first testing my code, I was getting the output you see in the attachment.  I then added the {}512.


Suggestions

1. Stop using this archaic AssignFile/Rewrite, and "File of Record" thing; instead use MemoryStream/FileStream

2. Consider using JSON as a storage format
1. OK, I'll start learning about MemoryStream/FileStream.

2. Where do I start with "using JSON with Pascal"?  All the examples use Python and C++ and even some Javascript.

Free Pascal Lazarus Version #: 2.2.4
Date: 24 SEP 2022
FPC Version: 3.2.2
Revision: Lazarus_2_2_4
x86_64-win64-win32/win64

paweld

  • Hero Member
  • *****
  • Posts: 1571
Re: Gooble-dee-gok in my data file...
« Reply #4 on: May 08, 2025, 08:52:14 am »
2. Where do I start with "using JSON with Pascal"?
Your code after changing to write to JSON might look like this, for example:
Code: Pascal  [Select][+][-]
  1. unit SimpNotes;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6. uses
  7.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Interfaces, FileUtil, ExtCtrls,
  8.   fpjson, fpjsonrtti;
  9.  
  10. type
  11.  
  12.   { TNotes }
  13.  
  14.   TNotes = class(TCollectionItem)
  15.   private
  16.     FContent: String;
  17.     FTitle: String;
  18.   published
  19.     property Title: String read FTitle write FTitle;
  20.     property Content: String read FContent write FContent;
  21.   end;
  22.  
  23.   { TNotesList }
  24.  
  25.   TNotesList = class(TCollection)
  26.   private
  27.     function GetItems(Index: Integer): TNotes;
  28.     procedure SetItems(Index: Integer; AValue: TNotes);
  29.   public
  30.     constructor Create;
  31.     function Add: TNotes;
  32.     procedure Add(ATitle, AContent: String);
  33.     procedure SaveAsJson(AFileName: String);
  34.     procedure LoadFromJson(AFileName: String);
  35.     property Items[Index: Integer]: TNotes read GetItems write SetItems;
  36.   end;
  37.  
  38.   { TForm1 }
  39.  
  40.   TForm1 = class(TForm)
  41.     BtnAdd: TButton;
  42.     BtnDelete: TButton;
  43.     BtnSave: TButton;
  44.     BtnMod: TButton;
  45.     BtnNew: TButton;
  46.     Memo1: TMemo;
  47.     Title: TEdit;
  48.     Label1: TLabel;
  49.     Label2: TLabel;
  50.     TitlesList: TListBox;
  51.     Content: TMemo;
  52.     procedure BtnAddClick(Sender: TObject);
  53.     procedure BtnDeleteClick(Sender: TObject);
  54.     procedure BtnModClick(Sender: TObject);
  55.     procedure BtnSaveClick(Sender: TObject);
  56.     procedure BtnNewClick(Sender: TObject);
  57.     procedure FormCreate(Sender: TObject);
  58.     procedure FormDestroy(Sender: TObject);
  59.     procedure TitlesListClick(Sender: TObject);
  60.   private
  61.     noteslist: TNotesList;
  62.   public
  63.   end;
  64.  
  65. var
  66.   Form1: TForm1;
  67.  
  68. implementation
  69. {$R *.lfm}
  70.  
  71. { TNotesList }
  72.  
  73. function TNotesList.GetItems(Index: Integer): TNotes;
  74. begin
  75.   Result := TNotes(inherited Items[Index]);
  76. end;
  77.  
  78. procedure TNotesList.SetItems(Index: Integer; AValue: TNotes);
  79. begin
  80.   Items[Index].Assign(AValue);
  81. end;
  82.  
  83. constructor TNotesList.Create;
  84. begin
  85.   inherited Create(TNotes);
  86. end;
  87.  
  88. function TNotesList.Add: TNotes;
  89. begin
  90.   Result := inherited Add as TNotes;
  91. end;
  92.  
  93. procedure TNotesList.Add(ATitle, AContent: String);
  94. var
  95.   ni: TNotes;
  96. begin
  97.   ni := Add;
  98.   ni.Title := ATitle;
  99.   ni.Content := AContent;
  100. end;
  101.  
  102. procedure TNotesList.SaveAsJson(AFileName: String);
  103. var
  104.   sl: TStringList;
  105.   js: TJSONStreamer;
  106. begin
  107.   sl := TStringList.Create;
  108.   js := TJSONStreamer.Create(nil);
  109.   js.Options := js.Options + [jsoTStringsAsArray, jsoUseFormatString];
  110.   try
  111.     sl.Text := js.CollectionToJSON(Self);
  112.     sl.SaveToFile(AFileName);
  113.   finally
  114.     js.Free;
  115.     sl.Free;
  116.   end;
  117. end;
  118.  
  119. procedure TNotesList.LoadFromJson(AFileName: String);
  120. var
  121.   sl: TStringList;
  122.   jds: TJSONDeStreamer;
  123. begin
  124.   Clear;
  125.   if not FileExists(AFileName) then
  126.     exit;
  127.   sl := TStringList.Create;
  128.   jds := TJSONDeStreamer.Create(nil);
  129.   try
  130.     sl.LoadFromFile(AFileName);
  131.     jds.JSONToCollection(sl.Text, Self);
  132.   finally
  133.     jds.Free;
  134.     sl.Free;
  135.   end;
  136. end;
  137.  
  138. { TForm1 }
  139.  
  140. procedure TForm1.BtnAddClick(Sender: TObject);
  141. begin
  142.   if Trim(Title.Text) = '' then
  143.   begin
  144.     ShowMessage('Please enter a title for the note.');
  145.     Exit;
  146.   end;
  147.   if Trim(Content.Text) = '' then
  148.   begin
  149.     ShowMessage('Please enter content for the note.');
  150.     Exit;
  151.   end;
  152.   noteslist.Add(Title.Text, Content.Text);
  153.   TitlesList.Items.Add(Title.Text);  // Add the title to the ListBox.
  154.   Label2.Caption := 'Notes: ' + IntToStr(noteslist.Count);  // Update the note counter display;
  155.   Title.Clear;
  156.   Content.Clear;
  157. end;
  158.  
  159. procedure TForm1.BtnModClick(Sender: TObject);
  160. var
  161.   I: Integer;
  162. begin
  163.   if TitlesList.ItemIndex >= 0 then
  164.   begin
  165.     I := TitlesList.ItemIndex;
  166.     noteslist.Items[I].Title := Title.Text;
  167.     noteslist.Items[I].Content := Content.Text;
  168.     TitlesList.Items[I] := Title.Text; // Update the listbox text
  169.   end;
  170. end;
  171.  
  172. procedure TForm1.BtnDeleteClick(Sender: TObject);
  173. var
  174.   I: Integer;
  175. begin
  176.   if TitlesList.ItemIndex >= 0 then
  177.   begin
  178.     I := TitlesList.ItemIndex;  // This is the highlighted Title in the ListBox
  179.     noteslist.Delete(I);
  180.     TitlesList.Items.Delete(I);  // This removes the Title to be deleted.
  181.     Label2.Caption := 'Notes: ' + IntToStr(noteslist.Count);  // Update the note counter display;
  182.     Title.Clear;
  183.     Content.Clear;
  184.   end;
  185. end;
  186.  
  187. procedure TForm1.BtnSaveClick(Sender: TObject);
  188. begin
  189.   noteslist.SaveAsJson('notes.json');
  190. end;
  191.  
  192. procedure TForm1.BtnNewClick(Sender: TObject);
  193. begin
  194.   Title.Clear;
  195.   Content.Clear;
  196. end;  // BtnNewClick
  197.  
  198. procedure TForm1.FormCreate(Sender: TObject);
  199. begin
  200.   noteslist := TNotesList.Create;
  201.   noteslist.LoadFromJson('notes.json');
  202.   Label2.Caption := 'Notes: ' + IntToStr(noteslist.Count);  // Update the note counter display;
  203. end;  // FormCreate
  204.  
  205. procedure TForm1.FormDestroy(Sender: TObject);
  206. begin
  207.   noteslist.Free;
  208. end;
  209.  
  210. procedure TForm1.TitlesListClick(Sender: TObject);
  211. begin
  212.   if TitlesList.ItemIndex >= 0 then
  213.   begin
  214.     Title.Text := noteslist.Items[TitlesList.ItemIndex].Title;
  215.     Content.Text := noteslist.Items[TitlesList.ItemIndex].Content;
  216.   end;
  217. end;  // Proc TitlesListClick
  218.  
  219. end.

More info: https://wiki.lazarus.freepascal.org/JSON and https://wiki.lazarus.freepascal.org/Streaming_JSON
Best regards / Pozdrawiam
paweld

Thaddy

  • Hero Member
  • *****
  • Posts: 18765
  • To Europe: simply sell USA bonds: dollar collapses
Re: Gooble-dee-gok in my data file...
« Reply #5 on: May 08, 2025, 05:32:41 pm »
Note that in the example that is included in your question, Filesize, given file = file of TNotes, can be calulated as FileSize(handle) * SizeOf(TNotes);
NOT divide, since FileSize returns the number of records in the file.
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

Fibonacci

  • Hero Member
  • *****
  • Posts: 788
  • Internal Error Hunter
Re: Gooble-dee-gok in my data file...
« Reply #6 on: May 08, 2025, 05:42:32 pm »
Your code after changing to write to JSON might look like this, for example:

This example is way too complex. He doesnt know what JSON is. Collections, RTTI, properties, streaming? Nah..



Use simple JSON objects and arrays.

Talk with GPT.

Here is a simple example. Play with it. Write "TJSON" then Ctrl+Space, see what is available. Right click on things -> Find declaration. Read the code.

Code: Pascal  [Select][+][-]
  1. uses fpjson, jsonparser;
  2.  
  3. var
  4.   notes: TJSONArray;
  5.   note: TJSONObject;
  6.   enum: TJSONEnum;
  7.   s: string;
  8.  
  9. begin
  10.   notes := TJSONArray.Create;
  11.  
  12.   note := TJSONObject.Create;
  13.   note.Add('title', 'note title');
  14.   note.Add('content', 'note content');
  15.   note.Add('letsget', TJSONObject.Create);
  16.   TJSONObject(note['letsget'])['deeper'] := TJSONString.Create('note 1');
  17.   notes.Add(note);
  18.  
  19.   note := TJSONObject.Create;
  20.   note['title'] := TJSONString.Create('another note');
  21.   note['content'] := TJSONString.Create('another note content');
  22.   note['letsget'] := TJSONObject.Create;
  23.   TJSONObject(note['letsget'])['deeper'] := TJSONString.Create('note 2');
  24.   notes.Add(note);
  25.  
  26.   s := notes.AsJSON;
  27.   writeln(s);
  28.   // ^ this is what you save to the file, as text
  29.  
  30.   writeln;
  31.  
  32.   // -------------------------------
  33.   // now read
  34.  
  35.   notes := TJSONArray(GetJSON(s));
  36.  
  37.   for enum in notes do begin
  38.     note := TJSONObject(enum.Value);
  39.     writeln('title = ', note.Get('title'), ' | content = ', note.Get('content'), ' | ', note.FindPath('letsget.deeper').AsString);
  40.   end;
  41.  
  42.   readln;
  43. end.
« Last Edit: May 08, 2025, 06:00:50 pm by Fibonacci »

 

TinyPortal © 2005-2018