0

Is there an efficient way to read the last line of a text file? Right now i'm simply reading each line with code like below. Then S holds the last line read. Is there a good way to grab that last line without looping through entire text file?

TStreamReader* Reader;
Reader = new TStreamReader(myfile);
while (!Reader->EndOfStream) 
{
 String S = Reader->ReadLine();
}
relayman357
  • 793
  • 1
  • 6
  • 30
  • 2
    Read the file backwards instead of forwards. Read a buffer from the end of the file. Scan the buffer for a line break. If not found, rewind and read the next higher buffer. Scan it. Repeat as needed until a line break is found or the front of the file is reached. If needed, the buffer reading can be optimized using a memory-mapped file view. – Remy Lebeau Aug 17 '19 at 06:32
  • I converted my comment into answer – Spektre Aug 20 '19 at 07:52

1 Answers1

1

Exactly as Remy Lebeau commented:

  1. Use file access functions FileOpen,FileSeek,FileRead

    look here for example of usage:

  2. load your file by chunks from end into memory

    so make a static buffer and load file into it from end by chunks ...

  3. stop on eol (end of line) usually CR,LF

    just scan for 13,10 ASCII codes or their combinations from end of chunk. Beware some files have last line also terminated so you should skip that the first time ...

    known eols are:

    13
    10
    13,10
    10,13
    
  4. construct line

    if no eol found add whole chunk to string, if found add just the part after it ...

Here small example:

int hnd,siz,i,n;
const int bufsz=256;                // buffer size
char buf[bufsz+1];
AnsiString lin;                     // last line output
buf[bufsz]=0;                       // string terminator
hnd=FileOpen("in.txt",fmOpenRead);  // open file
siz=FileSeek(hnd,0,2);              // obtain size and point to its end
for (i=-1,lin="";siz;)
    {
    n=bufsz;                        // n = chunk size to load
    if (n>siz) n=siz; siz-=n;
    FileSeek(hnd,siz,0);            // point to its location (from start)
    FileRead(hnd,buf,n);            // load it to buf[]
    if (i<0)                        // first time pass (skip last eol)
        {
        i=n-1; if (i>0) if ((buf[i]==10)||(buf[i]==13)) n--;
        i--;   if (i>0) if ((buf[i]==10)||(buf[i]==13)) if (buf[i]!=buf[i+1]) n--;
        }
    for (i=n-1;i>=0;i--)            // scan for eol (CR,LF)
     if ((buf[i]==10)||(buf[i]==13))
      { siz=0; break; } i++;        // i points to start of line and siz is zero so no chunks are readed after...
    lin=AnsiString(buf+i)+lin;      // add new chunk to line
    }
FileClose(hnd);                     // close file
// here lin is your last line
Spektre
  • 49,595
  • 11
  • 110
  • 380