Jump to content

String - find only the first instance


Recommended Posts

Hi there

I am looking to read a file backwards into an array then search for a particular string. When the string is found, I want to output it (at least when testing). I only want to output the first occurrence.

I've searched around this forum, combining code from different posts but it outputs every occurence of the string. How would I change this to do as stated above?

Thanks! :)

#include <file.au3>

$File_Name = "mylog.log"
$Find = "hello"

Dim $Records
If ReadFileBackwards("mylog.log", $Records) Then
For $x = $Records[0] To 1 Step - 1
if StringInStr($Records[$x], $Find) Then Msgbox(0,'Record:' & $x, $Records[$x])
Next
Else

EndIf
Exit

Func ReadFileBackwards($SFilepath, ByRef $AArray)
Local $HFile
 $HFile = FileOpen($SFilepath, 0)
 If $HFile = -1 Then
 SetError(1)
 Return 0
 EndIf
 $AArray = StringSplit( StringStripCR( FileRead($HFile, _
 FileGetSize($SFilepath))), @LF)
 FileClose($HFile)
 Return 1
EndFunc
Link to comment
Share on other sites

  • Developers

Something like this?

#include <file.au3>
$File_Name = "mylog.log"
$Find = "hello"
Global $Records
_FileReadToArray("mylog.log",$Records)
For $x = $Records[0] To 1 Step -1
    If StringInStr($Records[$x], $Find) Then
        MsgBox(0, 'Record:' & $x, $Records[$x])
        ExitLoop
    EndIf
Next

SciTE4AutoIt3 Full installer Download page   - Beta files       Read before posting     How to post scriptsource   Forum etiquette  Forum Rules 
 
Live for the present,
Dream of the future,
Learn from the past.
  :)

Link to comment
Share on other sites

That works Jos. Thank you! Basically what I'm trying to do is find a string, then find another string and determine which one is the latest entry in the log file. From that I will be able to set the status of a variable :)

Edited by kiffab
Link to comment
Share on other sites

  • Developers

That works Jos. Thank you! Basically what I'm trying to do is find a string, then find another string and determine which one is the latest entry in the log file. From that I will be able to set the status of a variable :)

Should be easy ... add the tests after the Next for what you want to test for:

#include <file.au3>
$File_Name = "mylog.log"
$Find1 = "hello"
$Find2 = "goodbye"
Global $Records, $Found1=False, $Found2=False 
_FileReadToArray("mylog.log",$Records)
For $x = $Records[0] To 1 Step -1
    If StringInStr($Records[$x], $Find1) Then $Found1 = $x
    If StringInStr($Records[$x], $Find2) Then $Found2 = $x
    If $Found1 and $Found2 then ExitLoop
Next

SciTE4AutoIt3 Full installer Download page   - Beta files       Read before posting     How to post scriptsource   Forum etiquette  Forum Rules 
 
Live for the present,
Dream of the future,
Learn from the past.
  :)

Link to comment
Share on other sites

Thanks Jos. I had done this using variables x and y in 2 separate for loops but this seems more efficient :)

Appreciate your help!

Should be easy ... add the tests after the Next for what you want to test for:

#include <file.au3>
$File_Name = "mylog.log"
$Find1 = "hello"
$Find2 = "goodbye"
Global $Records, $Found1=False, $Found2=False 
_FileReadToArray("mylog.log",$Records)
For $x = $Records[0] To 1 Step -1
    If StringInStr($Records[$x], $Find1) Then $Found1 = $x
    If StringInStr($Records[$x], $Find2) Then $Found2 = $x
    If $Found1 and $Found2 then ExitLoop
Next

Link to comment
Share on other sites

Jos,

I have created a GUI and included the function. If I call it outside the while loop, it works. That only lets me read the log once. I want to read it repeatedly, say every 1 minute. I tried using sleep command and placing it in the while loop. Also tried activating it by pressing the start button but no joy. Where am I going wrong?

Thanks.

#include <file.au3>
#include <GUIConstantsEx.au3>

Global $logFile = @scriptdir & "/mylog.log"
$find1= "server1on"
$find2 = "server1off"

$status = ""

Func Test()
Global $Records, $Found1=False, $Found2=False
_FileReadToArray($logFile,$Records)
For $x = $Records[0] To 1 Step -1
    If StringInStr($Records[$x], $find1) Then $Found1 = $x
    If StringInStr($Records[$x], $find2) Then $Found2 = $x
    If $Found1 and $Found2 then ExitLoop
Next
If $Found1 > $Found2 Then $status = "ON"
EndFunc

$GUI = GUICreate("App", 400, 400)
GUICtrlCreatePic('bg.bmp', 0, 0, 400, 400)
GuiCtrlSetState(-1,$GUI_DISABLE)
GUISetFont(9, 1, 3, "verdana")
GUISetState()

GUICtrlCreateLabel("server1", 10, 10, 80, 30)
GuiCtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)

GUICtrlCreateLabel($status, 100, 10, 80, 30)
GuiCtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)

$start = GUICtrlCreateButton("Start",10, 160, 80, 30)

While 1

    $msg = GUIGetMsg()
    Switch $Msg
        Case $GUI_EVENT_CLOSE
            Exit
        Case $start
            Test()
    EndSwitch

WEnd

;GUIDelete() ; not sure if this is needed

Exit
Link to comment
Share on other sites

_ArraySearch with Partial = 1 and Forward = 0 should be your one-liner for this.

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

Instead of the loop ;)

Sorry if I'm being stupid here... how would this work? Would I not need a for loop or something so the log file would be read every x seconds/minutes?

:)

Appreciate the help you guys have given me ;)

Link to comment
Share on other sites

Sorry if I'm being stupid here... how would this work? Would I not need a for loop or something so the log file would be read every x seconds/minutes?

....

Change AdlibRegister("MyAdlib", 6000) to AdlibRegister("MyAdlib", 60000) for every 60 seconds.

A test "mylog.log" file used.

Line 1
Hello 2
Line 3
Hello 4
Line 5
Hello 6
Line 7

An example.

#include <file.au3>
#include <Array.au3>

HotKeySet("{ESC}", "Terminate")

AdlibRegister("MyAdlib", 6000) ; 6000 millsec = 6 seconds

Global $sFile_Name = @ScriptDir & "\mylog.log"
Global $sFind1 = "hello"
Global $sFind2 = "line"

;Initial MsgBox
MsgBox(0, "Press ESC to Exit", 'Last "' & $sFind1 & '" is in the line "' & _FindInLog($sFile_Name, $sFind1) & '".' & @LF & _
        'Last "' & $sFind2 & '" is in the line "' & _FindInLogA($sFile_Name, $sFind2) & '".', 4)

While 1
    Sleep(50)
WEnd


Func MyAdlib()
    MsgBox(0, "Press ESC to Exit", 'Last "' & $sFind1 & '" is in the line "' & _FindInLog($sFile_Name, $sFind1) & '".' & @LF & _
            'Last "' & $sFind2 & '" is in the line "' & _FindInLogA($sFile_Name, $sFind2) & '".', 4)
EndFunc   ;==>MyAdlib

; _FindInLog() returns the line that $Find1 is found in.
Func _FindInLog($File_Name, $Find)
    Local $Records
    _FileReadToArray($File_Name, $Records)
    #cs
        For $x = $Records[0] To 1 Step -1
        If StringInStr($Records[$x], $Find1) Then $Found1 = $x
        If StringInStr($Records[$x], $Find2) Then $Found2 = $x
        If $Found1 And $Found2 Then ExitLoop
        Next
    #ce
    Local $iIndex = _ArraySearch($Records, $Find, 0, 0, 0, 1, 0)
    Return $Records[$iIndex]
EndFunc   ;==>_FindInLogA

;or

; _FindInLogA() returns the line that $Find1 is found in.
Func _FindInLogA($File_Name, $Find)
    Return StringRegExpReplace(FileRead($File_Name), "(?mis)^.*([^\v]*" & $Find & "[^\v]*).*", "\1")
EndFunc   ;==>_FindInLog

Func Terminate()
    AdlibUnRegister("MyAdlib")
    Exit 0
EndFunc   ;==>Terminate

Edit: Changed RegExp from "(?is)^.*\v([^\v]*" & $Find & "[^\v]*).*" to match first line.

Edit2: Changed RegExp from "(?is)^.*\v|\A([^\v]*" & $Find & "[^\v]*).*" to match first line.

Edited by Malkey
Link to comment
Share on other sites

Thanks for your input Malkey. I was working on this last night and almost have it working as I want. My only problem is the X button in the top right corner won't work.

I tried adding the lines:

Case $msg = $GUI_EVENT_CLOSE

ExitLoop

but I've had to comment these out as the application closes immediately when they are present. My Exit button works fine but I'd like the X to work too!

What am I missing? AdlibUnregister?

Thanks again!

#include <Array.au3>
#include <file.au3>
#include <GUIConstantsEx.au3>

$find1 = "on"
$find2 = "off"

Global $logFile = @scriptdir & "/mylog.log"
Global $Records

$GUI = GUICreate("App", 300, 400)
;GUICtrlCreatePic('bg.bmp', 0, 0, 300, 400) ;set the background image
GuiCtrlSetState(-1,$GUI_DISABLE) ;disable gui
GUISetFont(12, 600, 1, "verdana")

GUICtrlCreateLabel("Server 1", 10, 30, 100, 40)
GuiCtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)

$server1status = GUICtrlCreateLabel("Default", 10, 100, 80, 30)
GuiCtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)

$Start = GUICtrlCreateButton("Start", 20, 350, 80, 40)
$Exit = GUICtrlCreateButton("Exit", 130, 350, 80, 40)

Func Server1()

    _FileReadToArray($logFile,$Records)
    $iIndex1 = _ArraySearch($Records, $find1, 0, 0, 0, 1, 0)
    $iIndex2 = _ArraySearch($Records, $find2, 0, 0, 0, 1, 0)

    If $iIndex1 > $iIndex2 Then
        GUICtrlSetData($server1status, "On")
        Else
        GUICtrlSetData($server1status, "Off")
    EndIf

EndFunc

GUISetState()

while 1

    $msg = GUIGetMsg()

    Switch $msg
        case $Exit
            Exit
        case $start
            AdlibRegister("server1",10000)
        ;Case $msg = $GUI_EVENT_CLOSE
        ;   ExitLoop
    EndSwitch

WEnd

GUIDelete()

Exit
Link to comment
Share on other sites

That Case statement is used in in a Select ... EndSelect routine.

Use

Case  $GUI_EVENT_CLOSE
    ExitLoop

For a Switch....EndSwitch routine.

Fantastic. Thank you. I knew it was something silly :)

Basic logic is pretty much there. The only other thing I've noticed is that if you click "Start", it waits 10 seconds, then another 10 seconds before updating the status (20 secondS).

I've cut this down to 10 seconds by calling the function before the AdlibRegister:

case $start
    server1()
    AdlibRegister("server1",10000)

Is it possible to update the status as soon as start is pressed? i.e. press start - change status to what it should be then 10 seconds later read file again and repeat?

Cheers

Link to comment
Share on other sites

I have been playing with your script. It appears to be working fine.

#include <Array.au3>
#include <file.au3>
#include <GUIConstantsEx.au3>

Global $find1 = "on"
Global $find2 = "off"

Global $logFile = @ScriptDir & "/mylog.log"
Global $Records
Local $msg

Local $GUI = GUICreate("App", 300, 400)
GUISetFont(12, 600, 1, "verdana")

;GUICtrlCreatePic('bg.bmp', 0, 0, 300, 400) ;set the background image
;GUICtrlSetState(-1, $GUI_DISABLE) ; disable Pic control.

GUICtrlCreateLabel("Server 1", 10, 30, 100, 40)
GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)

Local $server1status = GUICtrlCreateLabel("Default", 10, 100, 80, 30)
GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)

Local $Start = GUICtrlCreateButton("Start Server Check", 20, 350, 130, 20)
GUICtrlSetFont(-1, 8, 600)

Local $Exit = GUICtrlCreateButton("Stop Server Check", 155, 350, 130, 20)
GUICtrlSetFont(-1, 8, 600)

GUISetState()

While 1
    $msg = GUIGetMsg()
    Switch $msg
        Case -3 ; $GUI_EVENT_CLOSE = -3, as declared in #include <GUIConstantsEx.au3>.
            ExitLoop
        Case $Start
            Server1()
            AdlibRegister("server1", 10000)
        Case $Exit
            GUICtrlSetData($server1status, "Default")
            AdlibUnRegister("server1")
    EndSwitch
WEnd

AdlibUnRegister("server1")


Func Server1()
    _FileReadToArray($logFile, $Records)
    Local $iIndex1 = _ArraySearch($Records, $find1, 0, 0, 0, 1, 0)
    Local $iIndex2 = _ArraySearch($Records, $find2, 0, 0, 0, 1, 0)
    ConsoleWrite("if " & $iIndex1 & " > " & $iIndex2 & ' then "on"' & @LF) ; Testing purposes only.
    If $iIndex1 > $iIndex2 Then
        GUICtrlSetData($server1status, "On")
    Else
        GUICtrlSetData($server1status, "Off")
    EndIf
EndFunc   ;==>Server1
Link to comment
Share on other sites

I have been playing with your script. It appears to be working fine.

Thanks Malkey. I've changed this slightly to use images which should update depending on the status. I've noticed they flicker when you click start (but remain at default setting) then after 10 seconds they will change to the correct image.

Not sure why the image doesn't change as soon as start is clicked.

Cheers.

Link to comment
Share on other sites

Thanks Malkey. I've changed this slightly to use images which should update depending on the status. I've noticed they flicker when you click start (but remain at default setting) then after 10 seconds they will change to the correct image.

Not sure why the image doesn't change as soon as start is clicked.

Cheers.

Calling the function twice when start is clicked seems to fix this. Not sure if that's bad practice or not, but it works. :)

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...