Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Tail: the last lines of a text file


  • Please log in to reply
18 replies to this topic
Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
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
}


pin
  • Guests
  • Last active:
  • Joined: --
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

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
#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


Tuncay
  • Members
  • 1945 posts
  • Last active: Feb 08 2015 03:49 PM
  • Joined: 07 Nov 2006
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
}


Tuncay
  • Members
  • 1945 posts
  • Last active: Feb 08 2015 03:49 PM
  • Joined: 07 Nov 2006
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

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
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.

Tuncay
  • Members
  • 1945 posts
  • Last active: Feb 08 2015 03:49 PM
  • Joined: 07 Nov 2006
Oh I did not looked at the date... how I came to this thread??

vla
  • Members
  • 48 posts
  • Last active: Apr 03 2014 05:58 AM
  • Joined: 12 May 2007
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.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
It is a function call, its result is not saved, unless you write something like
MyVar := StrTail(4,Text)


vla
  • Members
  • 48 posts
  • Last active: Apr 03 2014 05:58 AM
  • Joined: 12 May 2007
perfect! thank you so much, i love you all.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
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

}


guest3456
  • Guests
  • Last active:
  • Joined: --

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

SinTroN
  • Members
  • 26 posts
  • Last active: Mar 28 2012 02:44 AM
  • Joined: 12 Apr 2010
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.

r0k
  • Members
  • 35 posts
  • Last active: Apr 12 2012 10:00 AM
  • Joined: 24 Mar 2012
Claat tu, verata nik'tu ...

Trying to undo thread necro :twisted:

guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011

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 -->