Gooble-dee-gok in my data file... lazarus / 21-General ---------------------------------------------------------------------- OC DelGuy · 2025-05-07 05:37 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit SimpNotes;{$mode objfpc}{$H+}interfaceuses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Interfaces, FileUtil, ExtCtrls;type  { TForm1 }  TShortString = String[255]; // Fixed-length string type of 255 characters.  TContentString = String[255]; // Fixed-length string type of 255 characters.   TNotes = packed record  Title: TShortString;    Content: TContentString;  end;   TForm1 = class(TForm)    BtnAdd: TButton;    BtnDelete: TButton;    BtnSave: TButton;    BtnMod: TButton;    BtnNew: TButton;    Memo1: TMemo;    Title: TEdit;    Label1: TLabel;    Label2: TLabel;    TitlesList: TListBox;    Content: TMemo;    procedure BtnAddClick(Sender: TObject);    procedure BtnDeleteClick(Sender: TObject);    procedure BtnModClick(Sender: TObject);    procedure BtnSaveClick(Sender: TObject);    procedure BtnNewClick(Sender: TObject);    procedure FormCreate(Sender: TObject);    procedure TitlesListClick(Sender: TObject);  private    Note: array of TNotes;    NoteCount: Integer;  procedure LoadNotes;    procedure SaveNotes;  public  end;   var    Form1: TForm1; implementation{$R *.lfm}{ TForm1 }   Procedure TForm1.BtnAddClick(Sender: TObject);    Var      NewNote: TNotes;    Begin      If Trim(Title.Text) = '' Then        Begin          ShowMessage('Please enter a title for the note.');          Exit;      End;      If Trim(Content.Text) = '' Then        Begin          ShowMessage('Please enter content for the note.');          Exit;      End;      NewNote.Title := Title.Text;      NewNote.Content := Content.Text;      Inc(NoteCount);  // Increment the Note Counter.    SetLength(Note, NoteCount);  // Add a blank space for new Note      Note[NoteCount - 1] := NewNote;  // Add the new note.      TitlesList.Items.Add(NewNote.Title);  // Add the title to the ListBox.      Label2.Caption := 'Notes: ' + IntToStr(NoteCount);  // Update the note counter display;      Title.Clear;      Content.Clear;    End;   Procedure TForm1.BtnModClick(Sender: TObject);    Var      I: Integer;    Begin      If TitlesList.ItemIndex >= 0 Then        Begin          I := TitlesList.ItemIndex;          Note[I].Title := Title.Text;          Note[I].Content := Content.Text;          TitlesList.Items[I] := Title.Text; // Update the listbox text        End;    End;   Procedure TForm1.BtnDeleteClick(Sender: TObject);    Var      I: Integer;    Begin      If TitlesList.ItemIndex >= 0 Then        Begin          I := TitlesList.ItemIndex;  // This is the highlighted Title in the ListBox          TitlesList.Items.Delete(I);  // This removes the Title to be deleted.          Move(Note[I + 1], Note[I], (NoteCount - I - 1) * SizeOf(TNotes));  // Move the below records up one on the list.          Dec(NoteCount);  // Decrement the Note Counter.        SetLength(Note, NoteCount);  // Set the note array records to NoteCount records.          Label2.Caption := 'Notes: ' + IntToStr(NoteCount);  // Update the note counter display;          Title.Clear;          Content.Clear;        End;    End;   Procedure TForm1.BtnSaveClick(Sender: TObject);    Begin      SaveNotes;    End;   Procedure TForm1.BtnNewClick(Sender: TObject);    Begin      Title.Clear;      Content.Clear;    End;  // BtnNewClick   Procedure TForm1.FormCreate(Sender: TObject);    Begin      NoteCount := 0;  // Init the Note Count      LoadNotes;      Label2.Caption := 'Notes: ' + IntToStr(NoteCount);  // Update the note counter display;    End;  // FormCreate   Procedure TForm1.TitlesListClick(Sender: TObject);    Begin      If TitlesList.ItemIndex >= 0 Then        Begin          Title.Text := Note[TitlesList.ItemIndex].Title;          Content.Text := Note[TitlesList.ItemIndex].Content;        End;    End;  // Proc TitlesListClick   Procedure TForm1.LoadNotes;    Var      F: File of TNotes;      I, ErrorCode: Integer;    Begin      If FileExists('Notes.dat') Then        Begin          AssignFile(F, 'Notes.dat');          {$I-} // Disable I/O checking          Reset(F);          ErrorCode := IOResult;          Memo1.Lines.Add('Notes Opened: ' + IntToStr(ErrorCode));          {$I+} // Enable I/O checking          If ErrorCode <> 0 Then          Begin              ShowMessage('Error opening notes file: ' + SysErrorMessage(ErrorCode));              NoteCount := 0;  SetLength(Note, 0);  Exit;            End;           Try            NoteCount := {FileSize(F)}512 div SizeOf(TNotes);            Memo1.Lines.Add('FileSize: ' + IntToStr(FileSize(F)));            Memo1.Lines.Add('Size Of - TNotes: ' + IntToStr(SizeOf(TNotes)));          Memo1.Lines.Add('Note Count: ' + IntToStr(NoteCount));          SetLength(Note, NoteCount);            For I := 0 to NoteCount - 1 Do              Begin                {$I-} // Disable I/O checking              Read(F, Note[I]);                ErrorCode := IOResult;              Memo1.Lines.Add('Reading Notes: ' + IntToStr(ErrorCode));              {$I+} // Enable I/O checking                If ErrorCode <> 0 Then                  Begin                    ShowMessage('Error reading notes file at index: ' + IntToStr(I) + ' - ' + SysErrorMessage(ErrorCode));                    NoteCount := 0;                  SetLength(Note, 0);                    TitlesList.Clear;                  CloseFile(F);                    Exit;                End; // ErrorCode                TitlesList.Items.Add(Note[I].Title);              End; // For I        Finally            {$I-} // Disable I/O checking            CloseFile(F);            ErrorCode := IOResult;            {$I+} // Enable I/O checking            If ErrorCode <> 0 Then              Begin                ShowMessage('Error closing notes file: ' + SysErrorMessage(ErrorCode));              End;          End; // Finally        End // FileExists      Else        Begin          NoteCount := 0;          SetLength(Note, 0);        End; // Else    End; // Proc LoadNotes   Procedure TForm1.SaveNotes;    Var      F: File of TNotes;      I: Integer;    Begin      AssignFile(F, 'Notes.dat');      Rewrite(F);      For I := 0 to NoteCount - 1 Do      Begin          Write(F, Note[I]);        End;      CloseFile(F);  End;  // Proc SaveNotes end. Why does the SaveNotes produce this:    (What's all the gooble-dee-gok around my Notes?)? Code: Text  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i 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.                          ðz      (    +       ¶    ÿÿÿÿ    ê?          (    T     ðz            ‘d     °KlÍÿ          T     ðz      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      ðzM    ðz      N                                      ðz      8à    +     F     ÿÿÿÿ    ê?            (    N      ðzM    P!T    !h   °KlÍÿ          N      ðz     And when reading the file, why does the File Size = 2?   (see attachment)      Two is the number of records. Shouldn't it be 1024? ---------------------------------------------------------------------- Fibonacci · 2025-05-07 05:57 Quote from: OC DelGuy on May 07, 2025, 07:37:24 amWhy 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). Quote from: OC DelGuy on May 07, 2025, 07:37:24 amAnd when reading the file, why does the File Size = 2?   (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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i 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 ---------------------------------------------------------------------- Thaddy · 2025-05-07 07:44 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.... ---------------------------------------------------------------------- OC DelGuy · 2025-05-07 15:18 Quote from: Fibonacci on May 07, 2025, 07:57:31 amQuote from: OC DelGuy on May 07, 2025, 07:37:24 amAnd when reading the file, why does the File Size = 2?   (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. Quote from: Fibonacci on May 07, 2025, 07:57:31 amCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i 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. Quote from: Fibonacci on May 07, 2025, 07:57:31 amSuggestions 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. ---------------------------------------------------------------------- paweld · 2025-05-08 06:52 Quote from: OC DelGuy on May 07, 2025, 05:18:27 pm2. 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit SimpNotes; {$mode objfpc}{$H+} interfaceuses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Interfaces, FileUtil, ExtCtrls,  fpjson, fpjsonrtti; type   { TNotes }   TNotes = class(TCollectionItem)  private    FContent: String;    FTitle: String;  published    property Title: String read FTitle write FTitle;    property Content: String read FContent write FContent;  end;   { TNotesList }   TNotesList = class(TCollection)  private    function GetItems(Index: Integer): TNotes;    procedure SetItems(Index: Integer; AValue: TNotes);  public    constructor Create;    function Add: TNotes;    procedure Add(ATitle, AContent: String);    procedure SaveAsJson(AFileName: String);    procedure LoadFromJson(AFileName: String);    property Items[Index: Integer]: TNotes read GetItems write SetItems;  end;   { TForm1 }   TForm1 = class(TForm)    BtnAdd: TButton;    BtnDelete: TButton;    BtnSave: TButton;    BtnMod: TButton;    BtnNew: TButton;    Memo1: TMemo;    Title: TEdit;    Label1: TLabel;    Label2: TLabel;    TitlesList: TListBox;    Content: TMemo;    procedure BtnAddClick(Sender: TObject);    procedure BtnDeleteClick(Sender: TObject);    procedure BtnModClick(Sender: TObject);    procedure BtnSaveClick(Sender: TObject);    procedure BtnNewClick(Sender: TObject);    procedure FormCreate(Sender: TObject);    procedure FormDestroy(Sender: TObject);    procedure TitlesListClick(Sender: TObject);  private    noteslist: TNotesList;  public  end; var  Form1: TForm1; implementation{$R *.lfm} { TNotesList } function TNotesList.GetItems(Index: Integer): TNotes;begin  Result := TNotes(inherited Items[Index]);end; procedure TNotesList.SetItems(Index: Integer; AValue: TNotes);begin  Items[Index].Assign(AValue);end; constructor TNotesList.Create;begin  inherited Create(TNotes);end; function TNotesList.Add: TNotes;begin  Result := inherited Add as TNotes;end; procedure TNotesList.Add(ATitle, AContent: String);var  ni: TNotes;begin  ni := Add;  ni.Title := ATitle;  ni.Content := AContent;end; procedure TNotesList.SaveAsJson(AFileName: String);var  sl: TStringList;  js: TJSONStreamer;begin  sl := TStringList.Create;  js := TJSONStreamer.Create(nil);  js.Options := js.Options + [jsoTStringsAsArray, jsoUseFormatString];  try    sl.Text := js.CollectionToJSON(Self);    sl.SaveToFile(AFileName);  finally    js.Free;    sl.Free;  end;end; procedure TNotesList.LoadFromJson(AFileName: String);var  sl: TStringList;  jds: TJSONDeStreamer;begin  Clear;  if not FileExists(AFileName) then    exit;  sl := TStringList.Create;  jds := TJSONDeStreamer.Create(nil);  try    sl.LoadFromFile(AFileName);    jds.JSONToCollection(sl.Text, Self);  finally    jds.Free;    sl.Free;  end;end; { TForm1 } procedure TForm1.BtnAddClick(Sender: TObject);begin  if Trim(Title.Text) = '' then  begin    ShowMessage('Please enter a title for the note.');    Exit;  end;  if Trim(Content.Text) = '' then  begin    ShowMessage('Please enter content for the note.');    Exit;  end;  noteslist.Add(Title.Text, Content.Text);  TitlesList.Items.Add(Title.Text);  // Add the title to the ListBox.  Label2.Caption := 'Notes: ' + IntToStr(noteslist.Count);  // Update the note counter display;  Title.Clear;  Content.Clear;end; procedure TForm1.BtnModClick(Sender: TObject);var  I: Integer;begin  if TitlesList.ItemIndex >= 0 then  begin    I := TitlesList.ItemIndex;  noteslist.Items[I].Title := Title.Text;    noteslist.Items[I].Content := Content.Text;    TitlesList.Items[I] := Title.Text; // Update the listbox text  end;end; procedure TForm1.BtnDeleteClick(Sender: TObject);var  I: Integer;begin  if TitlesList.ItemIndex >= 0 then  begin    I := TitlesList.ItemIndex;  // This is the highlighted Title in the ListBox    noteslist.Delete(I);    TitlesList.Items.Delete(I);  // This removes the Title to be deleted.    Label2.Caption := 'Notes: ' + IntToStr(noteslist.Count);  // Update the note counter display;  Title.Clear;    Content.Clear;  end;end; procedure TForm1.BtnSaveClick(Sender: TObject);begin  noteslist.SaveAsJson('notes.json');end; procedure TForm1.BtnNewClick(Sender: TObject);begin  Title.Clear;  Content.Clear;end;  // BtnNewClick procedure TForm1.FormCreate(Sender: TObject);begin  noteslist := TNotesList.Create;  noteslist.LoadFromJson('notes.json');  Label2.Caption := 'Notes: ' + IntToStr(noteslist.Count);  // Update the note counter display;end;  // FormCreate procedure TForm1.FormDestroy(Sender: TObject);begin  noteslist.Free;end; procedure TForm1.TitlesListClick(Sender: TObject);begin  if TitlesList.ItemIndex >= 0 then  begin    Title.Text := noteslist.Items[TitlesList.ItemIndex].Title;    Content.Text := noteslist.Items[TitlesList.ItemIndex].Content;  end;end;  // Proc TitlesListClick end. More info: https://wiki.lazarus.freepascal.org/JSON and https://wiki.lazarus.freepascal.org/Streaming_JSON ---------------------------------------------------------------------- Thaddy · 2025-05-08 15:32 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. ---------------------------------------------------------------------- Fibonacci · 2025-05-08 15:42 Quote from: paweld on May 08, 2025, 08:52:14 amYour 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses fpjson, jsonparser; var  notes: TJSONArray;  note: TJSONObject;  enum: TJSONEnum;  s: string; begin  notes := TJSONArray.Create;   note := TJSONObject.Create;  note.Add('title', 'note title');  note.Add('content', 'note content');  note.Add('letsget', TJSONObject.Create);  TJSONObject(note['letsget'])['deeper'] := TJSONString.Create('note 1');  notes.Add(note);   note := TJSONObject.Create;  note['title'] := TJSONString.Create('another note');  note['content'] := TJSONString.Create('another note content');  note['letsget'] := TJSONObject.Create;  TJSONObject(note['letsget'])['deeper'] := TJSONString.Create('note 2');  notes.Add(note);   s := notes.AsJSON;  writeln(s);  // ^ this is what you save to the file, as text   writeln;   // -------------------------------  // now read   notes := TJSONArray(GetJSON(s));   for enum in notes do begin    note := TJSONObject(enum.Value);    writeln('title = ', note.Get('title'), ' | content = ', note.Get('content'), ' | ', note.FindPath('letsget.deeper').AsString);  end;   readln;end.