AutoHotkey Community

It is currently May 27th, 2012, 5:14 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 14 posts ] 
Author Message
PostPosted: December 16th, 2005, 3:37 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
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.
Code:
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
Code:
MsgBox % Tail(3, A_ScriptFullPath)
For only the last line of a file, the following 2-line script is enough
Code:
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.
Code:
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
}


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 16th, 2005, 4: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


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: December 16th, 2005, 5:45 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
Code:
#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


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 12th, 2009, 9:50 am 
Offline

Joined: November 7th, 2006, 9:47 pm
Posts: 1934
Location: Germany
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.autohotkey.com/forum/viewtopic.php?t=43104

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


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


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 12th, 2009, 10:55 am 
Offline

Joined: November 7th, 2006, 9:47 pm
Posts: 1934
Location: Germany
Here is a comparison of the functions.

Anyway here is my benchmark result:

Quote:
Code:
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:
Code:
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


Last edited by Tuncay on April 12th, 2009, 6:27 pm, edited 2 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 12th, 2009, 4:41 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 12th, 2009, 4:48 pm 
Offline

Joined: November 7th, 2006, 9:47 pm
Posts: 1934
Location: Germany
Oh I did not looked at the date... how I came to this thread??


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 14th, 2009, 8:12 pm 
Offline

Joined: May 12th, 2007, 8:29 am
Posts: 48
Code:
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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 14th, 2009, 8:51 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
It is a function call, its result is not saved, unless you write something like
Code:
MyVar := StrTail(4,Text)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 14th, 2009, 8:54 pm 
Offline

Joined: May 12th, 2007, 8:29 am
Posts: 48
perfect! thank you so much, i love you all.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 14th, 2009, 9:08 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
btw, you can write the original function more compactly
Code:
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
}


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 7th, 2010, 4:43 am 
Laszlo wrote:
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.com/forum/topic4619.html


Report this post
Top
  
Reply with quote  
PostPosted: April 16th, 2012, 1:30 am 
Offline

Joined: April 12th, 2010, 9:39 am
Posts: 26
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
Code:
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.


Report this post
Top
 Profile  
Reply with quote  
PostPosted: April 16th, 2012, 8:49 am 
Offline

Joined: March 24th, 2012, 9:54 am
Posts: 34
Claat tu, verata nik'tu ...

Trying to undo thread necro :twisted:


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 14 posts ] 

All times are UTC [ DST ]


Who is online

Users browsing this forum: Exabot [Bot], Stigg and 9 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group