#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Sep 2011
    Posts
    14
    Rep Power
    0

    Nubee errors - read/write filedata


    OK. It's just the initial hump I can't seem to get over (unless I am missing more...)

    It's been a while since I coded. I used to code text-based , never any gui-based . I thought I would start simple:

    Code:
    unit FirstEffort;  interface  uses   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,   Dialogs, Spin, StdCtrls, ExtCtrls;  type   TForm1 = class(TForm)     NameLbl: TLabel;     FirstName: TEdit;     AddrLbl: TLabel;     LastName: TEdit;     CityLbl: TLabel;     Address: TEdit;     LoadFile: TButton;     SaveFile: TButton;     City: TEdit;     procedure LoadFileClick(Sender: TObject);     procedure SaveFileClick(Sender: TObject);     procedure FirstNameChange(Sender: TObject);    private     { Private declarations }   public     { Public declarations }   end;  var   Form1: TForm1;  implementation  {$R *.dfm}  procedure TForm1.FirstNameChange(Sender: TObject); begin  end;  procedure TForm1.LoadFileClick(Sender: TObject); var   fileData, memoBox: TStringList;  begin   memoBox := TStringList.Create;   fileData := TStringList.Create;    try     try       fileData.LoadFromFile('c:\Misc\FirstEffort.txt');     except       on e:Exception do begin         ShowMessage('Loading file failed with error: ' + e.Message);       end;     end;    finally     if fileData.text <> '' then         memoBox := fileData;         showmessage(memoBox.Text);     end;   end;  (* fileData.Free; *)   procedure TForm1.SaveFileClick(Sender: TObject); var   fileData, memoBox: TStringList;  begin   fileData := TStringList.Create;  try try   fileData.Add(FirstName.Text);   fileData.Add(LastName.Text);   fileData.Add(Address.Text);   fileData.Add(City.Text);   except     on e:Exception do begin       ShowMessage('fileData.Add failed with error: ' + e.Message);     end; end; OutputDebugString('After Adds...Before ShowMessage ---------------');      showmessage(memoBox.Text); OutputDebugString('After ShowMessage ---------------');    try     fileData.SaveToFile('c:\Misc\FirstEffort.txt'); OutputDebugString('After Try ---------------');   except     on e:Exception do begin       ShowMessage('Saving file failed with error: ' + e.Message);     end;    end; finally OutputDebugString('After First Finally ---------------'); OutputDebugString('After ShowMessage ---------------');    fileData.Free; end;        end; end.
    Maybe it's not simple enough!

    If you try to run it, you'll see the problems I am faced with...

    Do appreciate ANY assistance.

    Thank you, in advance, for your time!

    < Steve >
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    350
    Rep Power
    7
    As written, I think your SaveFileClick will work, although you do declare a memoBox TStringList variable and never use it, so you may just want to remove that declaration. Your LoadFileClick procedure does have some issues...

    Code:
    procedure TForm1.LoadFileClick(Sender: TObject); 
    var   
       fileData, memoBox: TStringList;  
    begin
       memoBox := TStringList.Create; //(1*)
       fileData := TStringList.Create;//(1*)
       try     
          try
            fileData.LoadFromFile('c:\Misc\FirstEffort.txt');
          except
           on e:Exception do 
           begin
             ShowMessage('Loading file failed with error: ' + e.Message);
           end;
         end;
       finally
         if fileData.text <> '' then
             memoBox := fileData;//(2*)
         showmessage(memoBox.Text);
       end;
    end;
    1* You create two stringlists, but do not Free either one of them, so as written, this application will leak memory
    2* it seems you want to transfer the data read into fileData to memoBox. Unfortunately, memoBox := fileData does not accomplish that. Two ways to tranfer would be either: memoBox.Text := fileData.Text; or memoBox.Assign(fileData);

    Code:
    procedure TForm1.LoadFileClick(Sender: TObject); 
    var   
      fileData, memoBox: TStringList;  
    begin
      memoBox := TStringList.Create;  //allocate memory
      try  //protect memory allocation with try..finally block
        fileData := TStringList.Create;  //allocate memory
        try  //protect this memory allocation with its own try..finally block
          try
            fileData.LoadFromFile('c:\Misc\FirstEffort.txt');
            if fileData.text <> '' then
            begin
              memoBox.Assign(fileData;);
              showmessage(memoBox.Text);
            end;
          except
            on e:Exception do 
              ShowMessage('Loading file failed with error: ' + e.Message);
          end;
        finally
          fileData.Free;  //finally will ensure memory is freed even if an error happened
        end;
      finally
        memoBox.Free; //finally will ensure this memory is freed.       
      end;
    end;
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Sep 2011
    Posts
    14
    Rep Power
    0
    Thank you!

    I understand the lack of assignment of memoBox; the omission of the FREE (I deleted it); and, the transfer of data (.Text) from one file to another.

    A couple of questions, if I may:

    1) The second option for copying files (memoBox.Assign(fileData)) leads me to believe that the old text-based file ops are still functional. Is this correct?

    2) On the LoadFileClick procedure, should the "Try" be used before the TStringList.Create?

    Try/except/finally did not exist a few decades ago. [OpEd] I think it is a programmer's responsibility to use error-checking extensively. (Microsoft would have better products...)

    Allow me to compliment you on your comments. Professional accomplishment!

    Thank you for your time, majlumbo. I will try to get to it this weekend and let you know how successful I am.

    < Steve >
    Last edited by seblake; September 5th, 2013 at 07:16 PM. Reason: spelling
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    350
    Rep Power
    7
    Hi Steve,

    Thanks for the kind words, I've learned much from boards like this over the years, so I figure pay it forward....

    Anyway, here's my thoughts/answers for the questions you have...

    1) The second option for copying files (memoBox.Assign(fileData)) leads me to believe that the old text-based file ops are still functional. Is this correct?

    The fact is that the old "text-based file ops" are still functional, but the .Assign here is not the same as the AssignFile(XXX) you may be alluding to. To read up on AssignFile, check here , but even though they are available, you really shouldn't use them anymore. The correct approach is to use a TStream descendant, such as TFileStream, TMemoryStream. You can read about TFileStream here If you have questions on their use, just post a new question.

    2) On the LoadFileClick procedure, should the "Try" be used before the TStringList.Create?

    No, the try comes after. The concept is that, after you allocate memory, you set up a try..finally block to ensure that that memory is freed regardless of whether an error happens or not. If you put it before, then the finally will be run with the possibility that the error happened on the memory allocation, thus you'd attempt to free an object that never got allocated. Basically when the code enters the try of a try finally block, the "finally" block is guaranteed to run, come hell or high water.

    That said, a try..finally does NOT handle the error for you, it simply ensures the finally block runs not matter if an error does/or doesn't happen. That's why the finally block should have the least code possible, as in just the code to free the memory. You wouldn't want to introduce an error condition within your finally block with extraneous code....

    Try/except/finally did not exist a few decades ago. [OpEd] I think it is a programmer's responsibility to use error-checking extensively. (Microsoft would have better products...)

    Yes, I agree with you on error checking, but try except blocks can also catch errors you weren't expecting. Out of memory... Also, it's not uncommon to combine try finally within a try except (or try except within a try finally)

    Best of Luck.

    Al
    Last edited by majlumbo; September 6th, 2013 at 07:55 AM.
  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Sep 2011
    Posts
    14
    Rep Power
    0

    Still at it


    OK. So, the next step would be to increase the number of iterations (records) I "save." I can do an array[i to maxrec] or even with rec, do fileData.Add(names).

    But I can't seem to get my head around syntax v logic.

    The fileData.addz are in the Save routine. I believe... I only need to save the fileData.text when I am through with it. (Right?) So...I can put them in an AddEntry routine. Then...would the onclick event after the final add actually do anything? Or would it just increment the rec counter?

    Code:
    procedure TForm1.SaveFileClick(Sender: TObject); var   fileData: TStringList;  begin   fileData := TStringList.Create;    try  // Try1     try  // Try2       fileData.Add(FirstName.Text);       fileData.Add(LastName.Text);       fileData.Add(Address.Text);       fileData.Add(City.Text);     except       on e:Exception do         begin           ShowMessage('fileData.Add failed with error: ' + e.Message);         end;     end;  // Try2     try  // Try3       fileData.SaveToFile('c:\Misc\FirstEffort.txt');     except       on e:Exception do         begin           ShowMessage('Saving file failed with error: ' + e.Message);         end;     end; // Try3    finally     fileData.Free;     end;  // Try2   end; // Try1
    Still confused...

    Thanks.

    < Steve >
  10. #6
  11. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Sep 2011
    Posts
    14
    Rep Power
    0

    Code format


    How does your code get formatted and mine ends up on one line?????
  12. #7
  13. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    350
    Rep Power
    7
    My first question is: Which version of Delphi are you using? The newer the version, the more (and IMHO better) options you have to "save" your data to file. Depending on the answer, I can recommend better options.

    As you've got it coded now, when you call "SaveFileClick" it will create a stringlist, add 4 lines (first name, last name, address, city) save the file, overwriting any previous iteration, so you end up with just one name/address per file.

    You can alter that by getting the file name of the file you want to save to, if it exists, open it, then add, and if it doesn't exist, just save it.
    To do this,
    add a TOpenDialog component to your form
    In the object inspector,
    set the defaultext to *.txt
    Double click on the right column of the Filter property to open the Filter property editor
    Add text file in the left column and *.txt in the right column (add any other file types you want)

    Code:
    TForm1 = class(TForm)
      ...
      OpenDialog1: TOpenDialog;
      SaveFile: TBitBtn;
      SaveAsFile: TBitBtn;
      AddRecord: TBitBtn;
      procedure SaveFileClick(Sender: TObject);
      procedure SaveAsFileClick(Sender: TObject);
      procedure FormCreate(Sender: TObject);
      procedure FormDestroy(Sender: TObject);
      procedure AddRecordClick(Sender: TObject);
    private
      procedure SaveFile;
      procedure AddRecord(const LastName, FirstName, Address, City: String);
      ..
    end;
    
    ...
    
    implementation
    
    procedure TForm1.SaveFileClick(Sender: TObject);
    begin
       SaveFile;
    end;
    
    procedure TForm1.SaveAsFileClick(Sender: TObject);
    begin
       if OpenDialog1.Execute then
          if FileExists(OpenDialog1.FileName) then
          begin
             if MessageDlg('Overwrite '+OpenDialog1.FileName+'?', mtWarning, [mbYes, mbNo], 0) = mrYes then
                fileData.SaveToFile(OpenDialog1.FileName);
          end
          else
             fileData.SaveToFile(OpenDialog1.FileName);
    end;
    
    procedure TForm1.AddRecord(const LastName, FirstName, Address, City: String);
    begin
       fileData.Add(FirstName);
       fileData.Add(LastName);
       fileData.Add(Address);
       fileData.Add(City);
    end;
    
    procedure TForm1.AddRecordClick(Sender: TObject);
    begin
       AddRecord(LastName.Text, FirstName.Text, Address.Text, City.Text)
    end;
    
    procedure TForm1.SaveFile(Sender: TObject); 
    begin  
       if OpenDialog1.FileName <> '' then
          fileData.SaveToFile(OpenDialog1.FileName);
       else
          SaveAsFileClick(Sender);
    end;
    The down side to this approach is that there's no guarantee that the file you selected holds the type of data you want to save to, it just displays all text files in the directory.

    As I said, there are better options than saving to a stringlist, especially if you don't want the data in the file to be editable within Notepad, and at least have a reasonable expectation that the file selected in fact is of the correct type.

    I only need to save the fileData.text when I am through with it. (Right?) So...I can put them in an AddEntry routine
    You can approach this by creating the stringlist at the startup of your application (instead of the routine that actually saves the data), and save it when the application terminates followed by freeing the stringlist. My only concern with this approach is that if your user added a bunch of names, and then the computer crashes, all those additions would be lost. So actually, I would save on every edit, but the creation and destruction of the stringlist can still be within the formcreate and formdestroy methods.

    *************
    Sorry, I reworked the code. The last post just didn't work (this code was not tested - written in notepad, so there may be errors - but should give you the general idea). Although, you need to add new components to your form. An Add Record Button, a Save Button and a SaveAs button...
    Last edited by majlumbo; September 17th, 2013 at 04:04 PM.
  14. #8
  15. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Sep 2011
    Posts
    14
    Rep Power
    0
    'Mornin', Al (and the rest of ya)!

    I am currently using Delphi XE. Would like a mobile-capable version, but, at $4K+ (XE5), I can't afford to upgrade! (retired, fixed income, etc). Any capable/functional open source you know of?

    Do appreciate your insight. I'll work at the implementations you suggested. Gotta go get my car - new transmission... ;'(

    I'm refreshing my (lack of) knowledge by reworking/rewriting an invoice app I wrote in '89. To that regard, I would prefer to save to a non-text file. That's why I mentioned records. Baby steps, eh?

    As for the computer crashing, etc., saving on each entry (or every 10, maybe?) would be fine with me. Would that not have any impact on the hardware (HDD)? Or is that the old-school part of me asking?

    Thanks for your time!

    < Steve >

IMN logo majestic logo threadwatch logo seochat tools logo