Seek with text file

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Seek with text file

Zaaphod
I am trying to figure out how I can remove the last line of a text file to replace it with a new one.  I can do this with typed files with seek... but I am confused on how I could do this with just a text file.

If I use:

Var Myfile: Text;
       Mydata:String;

         Assign(myfile,'test.txt');
         append(myfile);
         Writeln(myfile,'New Line'+mydata);
         Close(myfile);
I can write to the end of the file, but I can't use seek to go to the end of the file minus one line.

If I try
Var Myfile: file of Text;
       Mydata:String;

         Assign(myfile,'test.txt');
         Reset(myfile);
         Seek(myfile,FILESIZE(myfile)-1);
         Writeln(myfile,'New Line'+mydata);
         Close(myfile);
I cannot use writeln with typed files....

so I tried with write...
Var Myfile: file of Text;
       Mydata:String;

         Assign(myfile,'test.txt');
         Reset(myfile);
         Seek(myfile,FILESIZE(myfile)-1);
         Write(myfile,'New Line'+mydata);
         Close(myfile);
I get Error: Incompatible types: got "ShortString" expected "Text"

So then I tried
Var Myfile: file of String;
       Mydata:String;

         Assign(myfile,'test.txt');
         Reset(myfile);
         Seek(myfile,FILESIZE(myfile)-1);
         Write(myfile,'New Line'+mydata);
         Close(myfile);

Well that compiles, but when I run it the new line at the end of my file is what I wanted but followed by whole long line of garbage... some of it looks like the contents of memory that was not part of the string.  I'm guessing it attached a string of length 255 even though the string was not that long.

Can someone please help me understand this.  I really have no concept of the difference between 'text' and 'string'  The file I am trying to work with is just a regular text file like you would create with notepad..  Is there some way to open this file and use Seek to get to the line number I want?  It seems to me like the file is very well defined, a CRLF at the end of each line defines how long each line is, it seems like I would be able to seek X number of these records... but I can't figure out how to define it... or write just the part of the string with my actual data in it.

If I can't use seek then what do I do? Open it, reset it, then read the entire file except the last line then somehow then write my new line?

Or do I have to read the whole file into a buffer or something, make my change, then write the whole thing out again?

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
rvk
Reply | Threaded
Open this post in threaded view
|

Re: Seek with text file

rvk
Op 03-04-2018 12:03 schreef James Richters:

> So then I tried
> Var Myfile: file of String;
>         Mydata:String;
>
>           Assign(myfile,'test.txt');
>           Reset(myfile);
>           Seek(myfile,FILESIZE(myfile)-1);
>           Write(myfile,'New Line'+mydata);
>           Close(myfile);
>
And what do you think FILESIZE(myfile) holds?
You would only strip the last character from the file with - 1.

Besides... file of String isn't a thing because String is a pointer to a
area with characters and you can't have a file of string. You could do
file of String[80] but in that case you don't have a normal text-file
anymore.

How large are the files?
The easiest is to just load it into a TStringList and replace the last line.

If you have very very large files you would need to scan for the last
line. You could do that from beginning to end but it would be better to
do it from end to begin and stopping at the first CRLF. You could do
that by opening it as stream of Text and stepping backwards in the file.

But a TStringList is really the easiest for small to medium text-files.

Rik
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Seek with text file

Anthony Walter-3
In reply to this post by Zaaphod
If the file is not to big you could simply write:

procedure ChangeLastLine(const FileName, NewLine: string);
var
  S: TStrings;
begin
  S := TStringList.Create;
  try
    S.LoadFromFile(FileName);
    if S.Count > 0 then
      S[S.Count - 1] := NewLine
    else
      S.Add(NewLine);
  finally
    S.Free;
  end;
end;

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
rvk
Reply | Threaded
Open this post in threaded view
|

Re: Seek with text file

rvk
Op 03-04-2018 13:04 schreef Anthony Walter:

> If the file is not to big you could simply write:
>
> procedure ChangeLastLine(const FileName, NewLine: string);
> var
>    S: TStrings;
> begin
>    S := TStringList.Create;
>    try
>      S.LoadFromFile(FileName);
>      if S.Count > 0 then
>        S[S.Count - 1] := NewLine
>      else
>        S.Add(NewLine);
>    finally
>      S.Free;
>    end;
> end;
Don't forget the S.SaveToFile(FileName) just above the finally-line
otherwise nothing is saved back to the file :D

Rik

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Seek with text file

Zaaphod
Thank you for the advice and for the example. I don't know what is considered a large file.. these files can be maybe about 1000 lines long, most will be less, would this solution be suitable for files of this size?

Is Tstringlist something like an array of strings?


-----Original Message-----
From: fpc-pascal [mailto:[hidden email]] On Behalf Of Rik van Kekem
Sent: Tuesday, April 03, 2018 7:20 AM
To: [hidden email]
Subject: Re: [fpc-pascal] Seek with text file

Op 03-04-2018 13:04 schreef Anthony Walter:

> If the file is not to big you could simply write:
>
> procedure ChangeLastLine(const FileName, NewLine: string); var
>    S: TStrings;
> begin
>    S := TStringList.Create;
>    try
>      S.LoadFromFile(FileName);
>      if S.Count > 0 then
>        S[S.Count - 1] := NewLine
>      else
>        S.Add(NewLine);
>    finally
>      S.Free;
>    end;
> end;
Don't forget the S.SaveToFile(FileName) just above the finally-line otherwise nothing is saved back to the file :D

Rik

_______________________________________________
fpc-pascal maillist  -  [hidden email] http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
rvk
Reply | Threaded
Open this post in threaded view
|

Re: Seek with text file

rvk
Op 03-04-2018 13:58 schreef James Richters:
> Thank you for the advice and for the example. I don't know what is considered a large file.. these files can be maybe about 1000 lines long, most will be less, would this solution be suitable for files of this size?
>
> Is Tstringlist something like an array of strings?

Yes, TStringList is a TList with strings.
Internally it's indeed an array of strings.

 From the code-snippet from Anthony you can see it's really easy to use.

1000 lines of about average 70 characters per lines would be
72*1000=70MB. For current computers with memory in the GB this is not
large at all. TStringList would also be fast to read individual files.

If you however would need to do this for say 2000 files in a row, it
could become slow and you would need to code it differently to make it
more efficient. But for just a few individual files this is the easiest
method.

Rik
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Seek with text file

Mark Morgan Lloyd-5
On 03/04/18 12:45, Rik van Kekem wrote:

>  From the code-snippet from Anthony you can see it's really easy to use.
> 1000 lines of about average 70 characters per lines would
> be72*1000=70MB.

72 kB, not MB. Big enough to give some very old systems problems if they
used a Windows component as an editor, but these days trivial.

Historically, things like "text" and "file of string[80]" have been
fraught because of OS-specific interpretations of what a line of text
was. These days there are still ambiguities because of UTF-8 etc.

--
Mark Morgan Lloyd
markMLl .AT. telemetry.co .DOT. uk

[Opinions above are the author's, not those of his employers or colleagues]
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Seek with text file

Zaaphod
In reply to this post by rvk
For my use, it seems to work very well.  I am creating a file where it pretty much appends a new entry to the end of a file,  but occasionally the process needs to over-write the previous entry.  This TStringlist is indeed very easy to work with this Tstringlist, especially since I was given an example, Thanks Anthony!   For this process I need to write the file out for every entry, but I can think of some other places I could use this and just build the entire file in the TStringList and just write it all out once I'm done.

Sometimes I need to remember I have GB of ram now, and everything doesn't have to fit in a few K of ram anymore 😊

-----Original Message-----
From: fpc-pascal [mailto:[hidden email]] On Behalf Of Rik van Kekem
Sent: Tuesday, April 03, 2018 8:17 AM
To: [hidden email]
Subject: Re: [fpc-pascal] Seek with text file

Op 03-04-2018 13:58 schreef James Richters:
> Thank you for the advice and for the example. I don't know what is considered a large file.. these files can be maybe about 1000 lines long, most will be less, would this solution be suitable for files of this size?
>
> Is Tstringlist something like an array of strings?

Yes, TStringList is a TList with strings.
Internally it's indeed an array of strings.

 From the code-snippet from Anthony you can see it's really easy to use.

1000 lines of about average 70 characters per lines would be 72*1000=70MB. For current computers with memory in the GB this is not large at all. TStringList would also be fast to read individual files.

If you however would need to do this for say 2000 files in a row, it could become slow and you would need to code it differently to make it more efficient. But for just a few individual files this is the easiest method.

Rik
_______________________________________________
fpc-pascal maillist  -  [hidden email] http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal