Lazarus

Free Pascal => General => Topic started by: JimR on June 18, 2019, 06:20:54 am

Title: Write to a new file.
Post by: JimR on June 18, 2019, 06:20:54 am
I'm running Windows 10, 64 bit, latest version 2.0.2 of Lazarus

I open a file, read it, write to new file, close it. 
No errors.
No new file in the default directory, or anywhere else for that matter.
I'm missing something so basic, but can't see it.

procedure TForm1.Button1Click(Sender: TObject);   
var InFile   : TextFile;
    OutFile  : TextFile;
    Lp1, Lp2 : Integer;
    Str      : String;
begin
AssignFile(InFile,'ResidentData.TXT');
ReSet(InFile);
AssignFile(OutFile,'NewDirectory.TXT');
ReWrite(OutFile);
{$I+}
if IOResult = 0 then Button1.Caption := 'Good';
ReadLn(InFile,Str);
WriteLn(OutFile,Str);
ReadLn(InFile,Str);
WriteLn(OutFile,Str);
for Lp1 := 1 to 207 do  // 207 lines.
  begin
  ReadLn(InFile,Str);
  WriteLn(OutFile,Str);
  end; // lp1.
//Flush(OutFile);
CloseFile(OutFile);
end;
Title: Re: Write to a new file.
Post by: Thaddy on June 18, 2019, 07:46:32 am
Code: Pascal  [Select][+][-]
  1. program test;
  2. {$ifdef mswindows}{$apptype console}{$endif}
  3. var InFile   : Text;
  4.     OutFile  : Text;
  5.     S        : String;
  6. begin
  7. {$I-}  // should be - for ioresult use, otherwise exceptions. See manual.
  8. Assign(InFile,'residentdata.txt');
  9. Reset(InFile);
  10. if IOResult = 0 then writeln('Infile open');
  11. Assign(OutFile,'newdirectory.txt');
  12. ReWrite(OutFile);
  13. if IOResult = 0 then writeln('outfile created');
  14. while not eof(infile) do
  15. begin
  16.   ReadLn(InFile,S);
  17.   writeln(OutFile,S);
  18. end;
  19. Close(Infile);
  20. Flush(OutFile);
  21. Close(OutFile);
  22. end.
Title: Re: Write to a new file.
Post by: bytebites on June 18, 2019, 07:56:57 am
Code: Pascal  [Select][+][-]
  1. for Lp1 := 1 to 207 do  // 207 lines.
or
Code: Pascal  [Select][+][-]
  1. while not eof(infile) do
Title: Re: Write to a new file.
Post by: JangelH on June 18, 2019, 07:59:12 am
Tested same conditions. Works ok.
¿Did you save your project?
Title: Re: Write to a new file.
Post by: Thaddy on June 18, 2019, 07:59:22 am
@bytebites Yes that's the same as in my demo.
Title: Re: Write to a new file.
Post by: rvk on June 18, 2019, 08:16:39 am
Shouldn't the {$I+} be {$I-} ?

At least in the code of Thaddy. Because IOResult only works when {$I} is -

In both codes I miss the {$I-}
Always use both {$I-} and {$I+} around the system call and then read out IOResult.

 (Never forget to read out IOResult and only read it once otherwise you'll get into trouble)

Code: Pascal  [Select][+][-]
  1. {$I-}
  2. AssignFile(InFile,'ResidentData.TXT');
  3. ReSet(InFile);
  4. {$I+}
  5. if IOResult <> 0 then
  6. begin
  7.   Button1.Caption := 'Bad opening source file';
  8.   exit;
  9. end;
  10. {$I-}
  11. AssignFile(OutFile,'NewDirectory.TXT');
  12. ReWrite(OutFile);
  13. {$I+}
  14. if IOResult <> 0 then
  15. begin
  16.   Button1.Caption := 'Bad opening destination file';
  17.   exit;
  18. end;
  19. Button1.Caption := 'Good';
Title: Re: Write to a new file.
Post by: Thaddy on June 18, 2019, 08:25:03 am
Shouldn't the {$I+} be {$I-} ?
Yes. I just corrected that. posts crossed.

Note I am not a big fan of assignfile etc. It is assign . Assignfile etc draws in another unit...that is not necessary everything is in system and the other unit simply uses aliases to system.assign etc..
That said: if the mode is objpas that objpas unit is drawn in anyway. but these routines are really annoying.
Title: Re: Write to a new file.
Post by: rvk on June 18, 2019, 08:30:47 am
Yes, and I would prefer to switch it back on before or after reading out IOResult so that other system calls will fail when encountering an error.

When you leave {$I-} (IOCheck} in the disabled state, the Close or Readln or Writeln might also encounter an error. The program won't catch that in the {$I-} state and just fill in IOResult. In which case the IOResult will trigger somewhere else in your program where the IOCheck is enabled resulting in very strange errors somewhere in your program.

https://www.freepascal.org/docs-html/prog/progsu38.html

If you leave the {$I-} you will also need to read the IOResult after read and write and close to make sure you handle any error from the system-calls.
Title: Re: Write to a new file.
Post by: Thaddy on June 18, 2019, 08:34:59 am
My approach is different: I usually have either {$I-} and perform all iochecks applicable, or {$I+} with exceptions and object filestreams.
So either lightweight old school all the way (especially for utilities) or use exceptions. But I can see your point.
Note that reading IOResult will destroy it and reset to 0. So I don't see the problem.
See https://www.freepascal.org/docs-html/rtl/system/ioresult.html

Snippet from objpas:
Code: Pascal  [Select][+][-]
  1. Procedure AssignFile(out f:File;p:pchar);
  2. begin
  3.   System.Assign (F,p);
  4. end; //etc
Since these are not inlined there is call overhead.

Note there is one noticable difference: AssignFile has overloads that allows you to set the codepage, which might be of interest to some but not to me, usually.
E.g:
Code: Pascal  [Select][+][-]
  1. Procedure AssignFile(out t:Text;p:pchar; aCodePage : TSystemCodePage);
  2. begin
  3.   System.Assign (T,p);
  4.   SetTextCodePage(T,aCodePage);
  5. end;

Title: Re: Write to a new file.
Post by: rvk on June 18, 2019, 08:44:38 am
My approach is different: I usually have either {$I-} and perform all iochecks applicable, or {$I+} with exceptions and object filestreams.
So either lightweight old school all the way (especially for utilities) or use exceptions. But I can see your point.
Note that reading IOResult will destroy it and reset to 0. So I don't see the problem.
See https://www.freepascal.org/docs-html/rtl/system/ioresult.html
Yes, that's also a way to go.
But in that case you should also read IOResult after (every) read and write.
Because if you don't you can get a read error (from reading) somewhere else on a perfectly good file because there was still a result from a previous read or write. And that's very hard to debug. So in that case you need to make sure to always handle the IOResult after every system call.

The resetting of IOResult isn't the problem then but the fact an IOResult will not reset with every call. So reset() won't set it to 0 if it is already <> 0 !!!
Title: Re: Write to a new file.
Post by: Thaddy on June 18, 2019, 08:48:22 am
That's what I wrote: you need to check IOresult everywhere, except for assign because that doesn't do IOResult, reset/rewrite do, as do close, flush etc.
That's correct. I wrote a more elaborate example here some years ago. I have added some comments to my previous post, we crossed.
Title: Re: Write to a new file.
Post by: JimR on June 18, 2019, 12:15:17 pm
Thanks for the input, but the problem is not the {$+} or {$I-}, no error shows up there before or after I made the suggested fixes.  The programs runs without errors, I can even read from the newly created OutFile and display in on the screen.  The problem is when the program ends, the OutFile is not saved to the disk.  Sorry, I was probably not clear in my definition of my problem. 
Title: Re: Write to a new file.
Post by: rvk on June 18, 2019, 12:21:18 pm
... or after I made the suggested fixes.
Please provide the code you have now (after the fixes) because your original code (with the {$I+} is plain wrong because IOResult doesn't work in the + state).

(Could you also put the code inside [code] [/code] tags, you can use the # above the editor. Thanks)
Title: Re: Write to a new file.
Post by: JimR on June 18, 2019, 12:56:55 pm
procedure TForm1.Button1Click(Sender: TObject);
var InFile   : TextFile;
    OutFile  : TextFile;
    Lp1, Lp2 : Integer;
    Str      : String;
    Ch       : Char;
begin
AssignFile(InFile,'ResidentData.TXT');
ReSet(InFile);
{$I+}
if IOResult <> 0 then
  begin
  Label1.Caption := 'bad input file';
  exit;
  end;
{$I-}
AssignFile(OutFile,'NewDirectory.TXT');
ReWrite(OutFile);
{$I+}
if IOResult <> 0 then
  begin
  Label2.Caption := 'bad output file';
  exit;
  end;
{$I-}
for Lp1 := 1 to 207 do  // 207 lines.
  begin
  ReadLn(InFile,Str);
  WriteLn(OutFile,Str);
  Label3.Caption := IntToStr(Lp1);
  end; // lp1.
Flush(OutFile);
Reset(OutFile);
ReadLn(OutFile,Str);
Label4.Caption := Str;
CloseFile(OutFile);
end;

Label2.caption and Label3.caption are both unwritten to, so no problems there.
Label3.caption is 207, so all 207 lines were read and written.
Label4.caption has the first line copied from source file, so that worked.
I can take out the ioresult lines of code with no change to anything.
The problem is after the procedure complete, and I look in the directory where there should be a new file called 'NewDirectory.TXT', there isn't one.
Title: Re: Write to a new file.
Post by: rvk on June 18, 2019, 01:03:32 pm
First off, you didn't put you code between [code] [/code] tags !!

Second... What is that Reset() on line 33 doing??
You didn't yet close the output file so you shouldn't read from it.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var InFile   : TextFile;
  3.     OutFile  : TextFile;
  4.     Lp1, Lp2 : Integer;
  5.     Str      : String;
  6.     Ch       : Char;
  7. begin
  8. AssignFile(InFile,'ResidentData.TXT');
  9. ReSet(InFile);
  10. {$I+}
  11. if IOResult <> 0 then
  12.   begin
  13.   Label1.Caption := 'bad input file';
  14.   exit;
  15.   end;
  16. {$I-}
  17. AssignFile(OutFile,'NewDirectory.TXT');
  18. ReWrite(OutFile);
  19. {$I+}
  20. if IOResult <> 0 then
  21.   begin
  22.   Label2.Caption := 'bad output file';
  23.   exit;
  24.   end;
  25. {$I-}
  26. for Lp1 := 1 to 207 do  // 207 lines.
  27.   begin
  28.   ReadLn(InFile,Str);
  29.   WriteLn(OutFile,Str);
  30.   Label3.Caption := IntToStr(Lp1);
  31.   end; // lp1.
  32. Flush(OutFile);
  33. Reset(OutFile); //???????????????
  34. ReadLn(OutFile,Str);
  35. Label4.Caption := Str;
  36. CloseFile(OutFile);
  37. end;
  38.  

And you didn't do a while loop for the 127 lines (which you should).
And you also don't close the infile.
And you have mismatching {$I-} and {$I+}
Every {$I-} should have a {$I+} and everything between will effect IOResult.
Your code is just inverted the {$I-} and {$I+} and leaves it at - for the read loop (which you should do the other way around).

Try this:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   InFile: TextFile;
  4.   OutFile: TextFile;
  5.   Str: string;
  6. begin
  7.   AssignFile(InFile, 'ResidentData.TXT');
  8.   {$I-} ReSet(InFile); {$I+}
  9.   if IOResult <> 0 then
  10.   begin
  11.     Label1.Caption := 'bad input file';
  12.     exit;
  13.   end;
  14.   AssignFile(OutFile, 'NewDirectory.TXT');
  15.   {$I-} ReWrite(OutFile); {$I+}
  16.   if IOResult <> 0 then
  17.   begin
  18.     Label2.Caption := 'bad output file';
  19.     CloseFile(InFile); // don't forget to close the infile
  20.     exit;
  21.   end;
  22.   while not EOF(InFile) do
  23.   begin
  24.     ReadLn(InFile, Str);
  25.     WriteLn(OutFile, Str);
  26.     Label3.Caption := 'no need for line count';
  27.   end;
  28.   CloseFile(OutFile);
  29.   CloseFile(InFile); // don't forget to close the infile
  30. end;
Title: Re: Write to a new file.
Post by: Thaddy on June 18, 2019, 02:20:20 pm
It is as rvk and me wrote:
https://www.freepascal.org/docs-html/current/prog/progsu38.html#x45-440001.2.38

It is always a good idea to examine the documentation
Title: Re: Write to a new file.
Post by: JimR on June 18, 2019, 09:22:50 pm
It works now.  Not sure why, but I'll take it.  I pulled all the IO checking and made a few other changes, sent the data to a listbox to make sure it was working right and it went there just fine.  Changed several other minor things and suddenly it wrote a file to my HD with the correct data.  So I want to say thanks to all who offered help.  You help is much appreciated.  Now I can continue to try and learn more on using this language going forward.

Thanks again.
Jim
Title: Re: Write to a new file.
Post by: lucamar on June 18, 2019, 10:45:10 pm
Just for curiosity I made a test using the code in this post above (https://forum.lazarus.freepascal.org/index.php/topic,45763.msg324021.html#msg324021) and despite its--small--shortcomings it does work in--at least in this system.

Results in the attached image.
TinyPortal © 2005-2018