Jump to content


Photo

Tail: the last lines of a text file


  • Please log in to reply
14 replies to this topic

#1 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 16 December 2005 - 02:37 PM

The last few lines of a text file are often needed, like at checking a log file for a specific entry. Here is a short script for it.
Tail(k,file)   ; Return the last k lines of file
{
   Loop Read, %file%
   {
      i := Mod(A_Index,k)
      L%i% = %A_LoopReadLine%
   }
   L := L%i%
   Loop % k-1
   {
      IfLess i,1, SetEnv i,%k%
      i--      ; Mod does not work here
      L := L%i% "`n" L
   }
   Return L
}
Test it with
MsgBox % Tail(3, A_ScriptFullPath)
For only the last line of a file, the following 2-line script is enough
Loop Read, %A_ScriptFullPath% 
   L = %A_LoopReadLine%
For small files the following version might be faster (Serenity's solution). It reads the file into a variable and parses it from memory. For large files the version above is better, because it scans the file only once, and keeps only k lines in memory.
Tail(k,file)   ; Return the last k lines of file
{
   FileRead text, %file%
   Loop Parse, text, `n
     lines++
   Loop Parse, text, `n
   {
      If (A_Index < lines - k)
         Continue
      L = %L%`n%A_Loopfield%
   }
   StringTrimLeft L, L, 1
   Return L
}


#2 pin

pin
  • Guests

Posted 16 December 2005 - 03:07 PM

Bookmarked!
Can i ask a question here? How to loop and send msgbox when file size is changed, or new lines are added to current %file%.
ie: when log.txt is changed(new lines are added) it send msgbox

#3 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 16 December 2005 - 04:45 PM

#Persistent

Settimer FileCheck



FileCheck:

   FileGetSize Size, %A_ScriptFullPath%   ; put your filename here

   If Size0 >= %Size%

      Return

   If Size0 =

   {

      Size0 = %Size%

      Return

   }

   MsgBox File size increased!

   Size0 = %Size%

Return


#4 Tuncay

Tuncay
  • Members
  • 1943 posts

Posted 12 April 2009 - 08:50 AM

I posted at an other thread a version without loop and regex. May be you are interested in testing. Not sure how faster (or slower) it can be.
http://www.autohotke...pic.php?t=43104

This is the one getting last line only:
str_getTailf(ByRef _Str) {
    Return SubStr(_Str,InStr(_Str,"`n",False,0)+1)
}

This is the one with n lines (here called _LineNum):
str_getTail(_Str, _LineNum = 1)
{
    StringGetPos, Pos, _Str, `n, R%_LineNum%
    StringTrimLeft, _Str, _Str, % ++Pos
    Return _Str
}


#5 Tuncay

Tuncay
  • Members
  • 1943 posts

Posted 12 April 2009 - 09:55 AM

Here is a comparison of the functions.

Anyway here is my benchmark result:

str_getTailf (by Tuncay):		188
str_getTail (by Tuncay):		469
StrTail /loop (by strictlyfocused02):	16750
Tail /regex (by Laszlo):		1062


This is the script and the functions used to benchmark:
Text := "`n`n`n"
Loop, 100000
    Text .= "`nLine " . A_Index

StartTime := A_TickCount
Loop, 100
str_getTailf(Text)
ElapsedTime1 := A_TickCount - StartTime

StartTime := A_TickCount
Loop, 100
str_getTail(Text)
ElapsedTime2 := A_TickCount - StartTime

StartTime := A_TickCount
Loop, 100
StrTail(1,Text)
ElapsedTime3 := A_TickCount - StartTime

StartTime := A_TickCount
Loop, 100
Tail(1,Text)
ElapsedTime4 := A_TickCount - StartTime

out =
(
str_getTailf (by Tuncay):`t`t%ElapsedTime1%
str_getTail (by Tuncay):`t`t%ElapsedTime2%
StrTail /loop (by strictlyfocused02):`t%ElapsedTime3%
Tail /regex (by Laszlo):`t`t%ElapsedTime4%
)
MsgBox, 1, Benchmark results, Ok copies to clipboard`n`n%out%
IfMsgBox Ok
    Clipboard := out

RETURN

str_getTailf(ByRef _Str) {
    Return SubStr(_Str,InStr(_Str,"`n",False,0)+1)
}

str_getTail(_Str, _LineNum = 1)
{
    StringGetPos, Pos, _Str, `n, R%_LineNum%
    StringTrimLeft, _Str, _Str, % ++Pos
    Return _Str
}

StrTail(k,str) ;; Inspired by Laszlo (http://www.autohotkey.com/forum/topic6928.html)
   {
   Loop,Parse,str,`n
      {
      i := Mod(A_Index,k)
      L%i% = %A_LoopField%
      }
   L := L%i%
   Loop,% k-1
      {
      If i < 1
         SetEnv,i,%k%
      i-- ;Mod does not work here
      L := L%i% "`n" L
      }
   Return L
   }

Tail(k,str) {
      Return RegExReplace(str,".*(?=(\n[^\n]*){" k "}$)")
}

edit: updated the Tail function from Laszlo

#6 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 12 April 2009 - 03:41 PM

The point of this 3.5 years old thread was to get the last line of a text file, without keeping the whole file in memory. Even if the file fits to the RAM, the memory usage gets large, which may cause other applications to slow down.

During the last three years we learned how to seek and read only portions of the file, so there are much faster methods for huge files.

#7 Tuncay

Tuncay
  • Members
  • 1943 posts

Posted 12 April 2009 - 03:48 PM

Oh I did not looked at the date... how I came to this thread??

#8 vla

vla
  • Members
  • 48 posts

Posted 14 May 2009 - 07:12 PM

Text =
(
Text of Line 1
Text of Line 2
Text of Line 3
Text of Line 4
Text of Line 5
Text of Line 6
Text of Line 7
Text of Line 8
Text of Line 9
Text of Line 10
Text of Line 11
Text of Line 12
Text of Line 13
Text of Line 14
Text of Line 15
)

MsgBox,% StrTail(4,Text)

;Return the last (k) lines of (str)
StrTail(k,str) ;; Inspired by Laszlo (http://www.autohotkey.com/forum/topic6928.html)
   {
   Loop,Parse,str,`n
      {
      i := Mod(A_Index,k)
      L%i% = %A_LoopField%
      }
   L := L%i%
   Loop,% k-1
      {
      If i < 1
         SetEnv,i,%k%
      i-- ;Mod does not work here
      L := L%i% "`n" L
      }
   Return L
   }

Using that code how do i get the var "text" to be a file? I do not fully understand this code, i am changing a few things to see what happens.
Changing the MsgBox,% StrTail(4,Text) into a 1 i get the last line, but what var is that saved under? %strtail?


i figured out the first question, use fileread. Still don't know exactly how the variable on that works.

#9 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 14 May 2009 - 07:51 PM

It is a function call, its result is not saved, unless you write something like
MyVar := StrTail(4,Text)


#10 vla

vla
  • Members
  • 48 posts

Posted 14 May 2009 - 07:54 PM

perfect! thank you so much, i love you all.

#11 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 14 May 2009 - 08:08 PM

btw, you can write the original function more compactly
Tail(k,file) {  ; Return the last k lines of file

   Loop Read, %file%

      i := Mod(A_Index,k), L%i% := A_LoopReadLine

   Loop % k

      i := Mod(i+1,k), L .= L%i% "`n"

   Return L

}


#12 guest3456

guest3456
  • Guests

Posted 07 June 2010 - 03:43 AM

During the last three years we learned how to seek and read only portions of the file, so there are much faster methods for huge files.


i found this one:

http://de.autohotkey.../topic4619.html

#13 SinTroN

SinTroN
  • Members
  • 26 posts

Posted 16 April 2012 - 12:30 AM

I'm not quite sure about `r`n combinations of line endings, but this should work
returns 0 if file is missing, else, returns needed amount of last strings, default is 1
fileGetLastLines(varFilename,varLines=1) {
	linecount:=0
	file:=FileOpen(varFilename, "r")
	if (not file)
		return 0
	Loop {
		file.Seek(0-A_Index, 2)
		line:=file.Read(1)
		if ((RegExMatch(line,"`n") or RegExMatch(line,"`r")) and not File.AtEOF)
			linecount++
	} until ((RegExMatch(line,"`n") or RegExMatch(line,"`r")) and not File.AtEOF and linecount=varLines)
	Loop {
		output.=file.Readline()
	} until (File.AtEOF)
	file.Close()
	return output
}
By the way, i've tested the way to loop through the whole 12MB size file, from the beginning till the end to get last string, it took 280-320ms on my Core i7 3.4Ghz system, and the method above always done at 0ms.

#14 r0k

r0k
  • Members
  • 35 posts

Posted 16 April 2012 - 07:49 AM

Claat tu, verata nik'tu ...

Trying to undo thread necro :twisted:

#15 guest3456

guest3456
  • Members
  • 1328 posts

Posted 11 June 2012 - 02:31 AM


During the last three years we learned how to seek and read only portions of the file, so there are much faster methods for huge files.


i found this one:

<!-- m -->http://de.autohotkey.../topic4619.html<!-- m -->


reposted here:
<!-- l --><a class="postlink-local" href="http://www.autohotkey.com/community/viewtopic.php?p=400494#p400494">viewtopic.php?p=400494#p400494</a><!-- l -->