Find two strings (in *memory)...

Just starting out? Need help? Post your questions and find answers here.
User avatar
Michael Vogel
Addict
Addict
Posts: 2819
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Find two strings (in *memory)...

Post by Michael Vogel »

While thinking about this thread Find a string in *memory, I'd need a super fast solution for finding two strings in a text file where the two strings have to be within a maximum distance of lines.

Let's say I am searching for the words 'MAIN' and 'NEARBY' and the maximum distance of the two words is 2:
- when a text file contains 'MAIN' in the lines 1, 10 and 20 and 'NEARBY' is in the lines 5, 15 and 25, the searching would have a negative result
- when a text file contains 'MAIN' in the lines 1, 10 and 20 and 'NEARBY' is in the lines 5, 15 and 18, the searching would return line 20 as a hit

I could search for the MAIN only and scan again for NEARBY seperately, but it would be slower than doing everything in a big loop.

I started a simple code but it is to simple (and fails in many cases)...

Code: Select all

String.s=ReplaceString("This is a test.Here's a simple line.And another line.A line with 'xxx'.A line with 'hehello',this should be found.And so on.And so on.Okay, one more line.And a last hello",".",#CRLF$)

String=LCase(String);					just for testing to ignore case issues for the moment...

Main.s="hello"
;Main.s="hehe"
Near.s="line"

*Memory.Character=@String;				Buffer
*Main.Character=@Main;					Main (never empty)
*Near.Character=@Near;					Nearby
*ScanMain.Character
*ScanNear.Character

NearDistance=2;						maximum ine distance
LineCount=1

If Bool(*Near\c);						Nearby searching...

	Repeat
		c=*Memory\c
		If c=#CR
			LineCount+1
		EndIf
		If ScanMain
			If c=*ScanMain\c;
				;Debug "ok "+Chr(c)
				If c=0
					FoundMain=LineCount
					Debug "FOUND MAIN at the end"
				Else
					*ScanMain+SizeOf(Character)
				EndIf
			ElseIf *ScanMain\c
				ScanMain=#Null
			Else
				If FoundNear>=LineCount-NearDistance
					Debug "B I N G O"
				EndIf
				Debug "FOUND MAIN "+c+"/"+*ScanMain\c+" at line "+LineCount
				ScanMain=#Null
				FoundMain=LineCount
			EndIf
		EndIf

		If ScanNear
			If c=*ScanNear\c;
				;Debug "ok "+Chr(c)
				If c=0
					FoundNear=LineCount
					Debug "FOUND at the end"
				Else
					*ScanNear+SizeOf(Character)
				EndIf
			ElseIf *ScanNear\c
				ScanNear=#Null
			Else
				If FoundMain>=LineCount-NearDistance
					Debug "B I N G O"
				EndIf
				Debug "FOUND NEAR "+c+"/"+*ScanNear\c+" at line "+LineCount
				ScanNear=#Null
				FoundNear=LineCount
			EndIf
		EndIf
		If c
			If c=*Main\c
				*ScanMain=*Main+SizeOf(Character)
				Debug "Start Main at line "+LineCount
				ScanMain=#True
			EndIf
			If c=*Near\c
				*ScanNear=*Near+SizeOf(Character)
				Debug "Start Near at line "+LineCount
				ScanNear=#True
			EndIf
		Else
			Break
		EndIf

		*Memory+SizeOf(Character)
	ForEver

	Debug "*"

Else

	Debug "No Nearby find needed..."

EndIf
infratec
Always Here
Always Here
Posts: 7662
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Find two strings (in *memory)...

Post by infratec »

No, searching a second time for the next word costs no extra time with my code, since you can set the start address.
Look at the loop in the examples.
pjay
Enthusiast
Enthusiast
Posts: 277
Joined: Thu Mar 30, 2006 11:14 am

Re: Find two strings (in *memory)...

Post by pjay »

Is the text file small enough to fit in memory in one go?
User avatar
Michael Vogel
Addict
Addict
Posts: 2819
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Find two strings (in *memory)...

Post by Michael Vogel »

pjay wrote: Sun Apr 07, 2024 11:37 am Is the text file small enough to fit in memory in one go?
Yes, the text will be loaded into memory.
infratec wrote: Sun Apr 07, 2024 8:45 am No, searching a second time for the next word costs no extra time with my code, since you can set the start address.
Look at the loop in the examples.
It's not that easy as you don't know which of the findings will be one "nearby' a second hit. The brute force method would do a full search by creating a list with all findings and then another one to check for 'matching pairs' - a simple program but not very fast.
infratec
Always Here
Always Here
Posts: 7662
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Find two strings (in *memory)...

Post by infratec »

Is your example correct?
You are talking from lines, but they are not separated by CR or LF. There are only dots between.
So these are sentences but not lines. But there is a CRLF$ at the end.

What is the real stuff?
Else I can not provide an example.
User avatar
Michael Vogel
Addict
Addict
Posts: 2819
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Find two strings (in *memory)...

Post by Michael Vogel »

The given program does show a practical example including CRLFs (ReplaceString), anyhow some fine tuning will have do be done later on:
- case sensitive/insensitive search (which I believe can done by a predefined character array finally)
- CR or CRLF could be given in the text which shouldn't be a big deal but who knows

For doing the 'brute force' method, I've started a quick test to do a find which returns the found text position, pointer to the start/end of the text lines (starting two text lines before and ending two text lines below). This would allow to search exactly within this memory range for the second search text (which I have called 'nearby').

Code: Select all

; Define

	#JumpToLineEnd=#True

	#CharByte=SizeOf(Character)
	#CharMult=#CharByte>>1
	
	Global Dim LineList(2)
	Global LineListCount
	
; EndDefine

Procedure FindMemoryString(*Content.Character,*Search.Character,*SkipChars.integer,*LineCount.Integer)

	Protected *Compare.Character
	Protected *Memory.Character
	Protected *Memlook.Character
	Protected LineListToggle
	
	*Memory=*Content+*SkipChars\i<<#CharMult
	*Compare=*Search
	
	LineListCount=0
	LineList(0)=0
	LineList(1)=0
	LineList(2)=0
	
	While *Memory\c
		If *Memory\c=#CR
			LineListCount=(LineListCount+1)%3
			LineList(LineListCount)=*Memory
			*LineCount\i+1
		EndIf
		If *Memory\c=*Compare\c
			*Memlook=*Memory
			Repeat
				*Memlook+#CharByte
				*Compare+#CharByte
				c=*Compare\c
			Until *Memlook\c<>c Or c=#Null
			If c=#Null
				*SkipChars\i=(*Memory-*Content)>>#CharMult
				CompilerIf #JumpToLineEnd
					*Memory+#CharByte
					While *Memory\c And *Memory\c<>#CR
						*Memory+#CharByte
					Wend
					ProcedureReturn (*Memory-*Content)>>#CharMult
				CompilerElse
					ProcedureReturn #True
				CompilerEndIf
			EndIf
			*Compare=*String
		EndIf
		*Memory+#CharByte
	Wend

	ProcedureReturn #Null

EndProcedure


SourceText.s

Line$=Space(100)+#CRLF$
For i=1 To 100
	SourceText+Str(i)+Line$
Next i
SourceText+"101 Rosi Hello World of..."+#CRLF$+"102"+Space(10000)+#CRLF$+"103 Rosi."


line.i=1

newpos=FindMemoryString(@SourceText,@"Rosi",@Pos,@Line)

Debug "Lines 1 to 3 of search range for 'nearby' text..."
Debug LineList((LineListCount+1)%3)
Debug LineList((LineListCount+2)%3)
Debug LineList((LineListCount)%3)

Debug "["+ReplaceString(PeekS(LineList((LineListCount+1)%3)+#CharByte,20),#LF$,"")+"]"
Debug "["+ReplaceString(PeekS(LineList((LineListCount+2)%3)+#CharByte,20),#LF$,"")+"]"
Debug "["+ReplaceString(PeekS(LineList((LineListCount)%3)+#CharByte,20),#LF$,"")+"]"

Debug ""
Debug "Continue searching CRs starting at "+newPos+" ("+PeekC(@SourceText+newpos<<#CharMult)+"<>0)"
;Debug "If 'nearby' search failes, search for 'main' will continue also from here..."
Debug "Found text at ("+Line+"): '"+PeekS(@SourceText+Pos<<#CharMult,10)+"'"
Debug ""

Debug "Lines 4 and 5 of search range for 'nearby' text..."
old=newpos
pos=newpos+#CharByte
cr.s=#CR$
newpos=FindMemoryString(@SourceText,@cr,@Pos,@Nil)
Debug "["+ReplaceString(PeekS(@SourceText+old<<#CharMult+#CharByte,20),#LF$,"")+"]"
Debug "["+ReplaceString(PeekS(@SourceText+pos<<#CharMult+#CharByte,20),#LF$,"")+"]"

User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Re: Find two strings (in *memory)...

Post by charvista »

I hope this solves your problem....
It uses your first example (where I changed "hello" to "main" and "line" to "near" for easiness.
Then I put it enlarged in a *Memory.
The final result is outputting all the Nearby.
Hopefully it is what you need :wink:

Code: Select all

Define *Offset,*MyString,*SearchStr
Define.i i,j,NearDistance,Factor,q0,q1,ms,occMain,occNear
Define.string MemPosMain, MemPosNear
Define.s String,Main,Near,AllNears

#SEP=Chr(166)

Procedure.i FindMemoryAscii(*Memory,*ToFind.Ascii,*MemPos.string,FindFromEnd.i=#False,MemoryLength.i=0,ToFindLength.i=0,ReturnOffset.i=#False,*StartAddress.Ascii=#Null)
    Protected.i ByteLength, Result
    Protected *MemEnd, *Ptr.Ascii, *FoundAt, *Ptr2.Ascii, *ToFindTmp.Ascii, *ToFindEnd
    If ToFindLength = 0
        ByteLength = MemorySize(*ToFind)
    Else
        ByteLength = ToFindLength
    EndIf
    *ToFindTmp = *ToFind
    *ToFindEnd = *ToFind + ByteLength
    If FindFromEnd
        If *StartAddress
            *Ptr = *StartAddress - ByteLength
        Else
            *Ptr = *Memory + MemorySize(*Memory) - ByteLength
        EndIf
        Repeat
            If *Ptr\a = *ToFindTmp\a
                *Ptr2 = *Ptr + 1
                *ToFindTmp + 1
                While *Ptr2\a = *ToFindTmp\a
                    *Ptr2 + 1
                    *ToFindTmp + 1
                    If *ToFindTmp = *ToFindEnd
                        *FoundAt = *Ptr
                        Break 2
                    EndIf
                Wend
                *ToFindTmp = *ToFind
            EndIf
            *Ptr - 1
        Until *Ptr <= *Memory
    Else
        *MemEnd = *Memory + MemorySize(*Memory) - ByteLength
        If *StartAddress
            If *StartAddress\a = *ToFind\a
                *Ptr = *StartAddress + 1
            Else
                *Ptr = *StartAddress
            EndIf
        Else
            *Ptr = *Memory
        EndIf
        Repeat
            If *Ptr\a = *ToFindTmp\a
                *Ptr2 = *Ptr + 1
                *ToFindTmp + 1
                While *Ptr2\a = *ToFindTmp\a
                    *Ptr2 + 1
                    *ToFindTmp + 1
                    If *ToFindTmp = *ToFindEnd
                        *FoundAt = *Ptr
                        Break 2
                    EndIf
                Wend
                *ToFindTmp = *ToFind
            EndIf
            *Ptr + 1
        Until *Ptr >= *MemEnd
    EndIf
    If ReturnOffset
        If *FoundAt
            *MemPos\s+Str(*FoundAt - *Memory)+#SEP; string containing all the found Offsets
        EndIf
        Result = *FoundAt - *Memory
    Else
        If *FoundAt
            *MemPos\s+Str(*FoundAt - *Memory)+#SEP; string containing all the found Offsets
        EndIf
        Result = *FoundAt
    EndIf
    ProcedureReturn Result
EndProcedure

Procedure.i Occur(String.s,StringToFind.s)
    Protected.i Pos,StartPosition,OccurCnt
    StartPosition=1
    Repeat
        Pos=FindString(String,StringToFind,StartPosition)
        If Pos
            OccurCnt+1
            StartPosition=Pos+Len(StringToFind)
        EndIf
    Until Pos=0
    ProcedureReturn OccurCnt
EndProcedure

; START PROGRAM
String.s=ReplaceString("This is a test.Here's a simple near.And another near.A near with 'xxx'.A near with 'hemain',this should be found.And so on.And so on.Okay, one more near.And a last main",".",#CRLF$)
String=LCase(String);					just for testing to ignore case issues for the moment...
Debug "The string is:"
Debug "======================================================================"
Debug String
Debug "======================================================================"
; making the String artificially longer
For i=1 To 10
    String+String
Next i
Debug "=> String is now "+Str(Len(String))+" bytes long."
Debug "======================================================================"


; now loading the string into *Memory
Factor=5
*MyString=AllocateMemory(Len(String)*Factor+1); load the string in *Memory 5 times larger (this should become 901'120 bytes long (+1 for the terminator)
For i=1 To Factor
    PokeS(*MyString+Len(String)*(i-1),String,Len(String),#PB_Ascii)
Next i
Debug "LOADED."
q0=ElapsedMilliseconds()
Debug "=> String is now "+Str(Len(String)*Factor)+" bytes long in *Memory."
Debug "======================================================================"
Main.s="main"
Near.s="near"
NearDistance=2; maximum line distance

*SearchStr=Ascii(Main)
MemPosMain\s=""
*Offset=0
Debug "SEARCHING FOR MAIN..."
Repeat
    *Offset=FindMemoryAscii(*MyString,*SearchStr,@MemPosMain,#False,0,MemorySize(*SearchStr)-1,#False,*Offset)
Until Not *Offset

*SearchStr=Ascii(Near)
MemPosNear\s=""
*Offset=0
Debug "SEARCHING FOR NEAR..."
Repeat
    *Offset=FindMemoryAscii(*MyString,*SearchStr,@MemPosNear,#False,0,MemorySize(*SearchStr)-1,#False,*Offset)
Until Not *Offset
Debug "..."
Debug "Main found at "+MemPosMain\s
occMain=Occur(MemPosMain\s,#SEP)
Debug "==> Total "+Str(occMain)+" occurence(s))"
Debug "..."
Debug "Near found at "+MemPosNear\s
occNear=Occur(MemPosNear\s,#SEP)
Debug "==> Total "+Str(occNear)+" occurence(s))"
Debug "..."
Dim M(occMain)
For i=1 To ArraySize(M())
    M(i)=Val(StringField(MemPosMain\s,i,#SEP))
Next i
; Dim N(occNear)
; For i=1 To ArraySize(N())
;     N(i)=Val(StringField(MemPosNear\s,i,#SEP))
; Next i    


Debug "NOW SEARCH FOR THOSE WHICH ARE NEAR, DEPENDING ON NEARDISTANCE="+Str(NearDistance)
For i=1 To ArraySize(M())
    For j=i-NearDistance To i+NearDistance
        If FindString(#SEP+MemPosNear\s,#SEP+Str(j)+#SEP)
            Debug "NEAR FOUND @ "+Str(j)
            AllNears+Str(j)+#SEP
            i+NearDistance*2
            Break
        EndIf
    Next j
Next i
q1=ElapsedMilliseconds()
ms=q1-q0
MessageRequester("Time needed to find all Nears",StrD(ms/1000,2)+" seconds")
MessageRequester("Here are all Nears",AllNears)
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Re: Find two strings (in *memory)...

Post by charvista »

@infratec
You are talking from lines, but they are not separated by CR or LF. There are only dots between.

Code: Select all

String.s=ReplaceString("This is a test.Here's a simple line.And another line.A line with 'xxx'.A line with 'hehello',this should be found.And so on.And so on.Okay, one more line.And a last hello",".",#CRLF$)
The code modifies all dots to #CRLF$ with ReplaceString() :mrgreen: :mrgreen: :mrgreen: Smart way :mrgreen:
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
User avatar
Michael Vogel
Addict
Addict
Posts: 2819
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Find two strings (in *memory)...

Post by Michael Vogel »

Thanks for all your responses, the approach of charvista to collect all founds is a nice one. Meanwhile I tried to put both searches into one loop to avoid scanning the same lines twice.

There's still a problem keeping the CRs of the previous lines in memory (so the two if 0 sections are buggy and I need to to an additional scan once in the ShowLines procedure)...

Code: Select all

; Define

	EnableExplicit

	#CharByte=SizeOf(Character);	1 Byte (Ascii) oder 2 Byte (Unicode)
	#CharShft=#CharByte>>1;		Chars<<0=Bytes oder Chars<<1=Bytes
	#CharList=1<<(8<<#CharShft)-1

	Enumeration
		#NearbyLineRange=2
		#NearbyEndOfLine
		#NearbyStructureArray
		#NearbyAllLines=#NearbyLineRange*2+1
	EndEnumeration

	Structure SearchType
		Memory.i
		State.i
		Line.i
		LastFinding.i
		CRs.i[#NearbyStructureArray]
		Keyword.s
	EndStructure

	EnumerationBinary
		#SearchEndOfString;		1
		#SearchSuccessful;		2
	EndEnumeration

	Global MainSearch.SearchType
	Global NearSearch.SearchType

	Global Dim NoCase(#CharList)

	Procedure Max(a,b)

		If a>b
			ProcedureReturn a
		Else
			ProcedureReturn b
		EndIf

	EndProcedure


; EndDefine
Procedure IndexBack(line)

	; Memory position to start next search (Back track #NearbyLineRange)

	If line<#NearbyEndOfLine
		ProcedureReturn #Null
	Else
		ProcedureReturn line % #NearbyEndOfLine
	EndIf

EndProcedure
Procedure IndexLine(line)

	; Memory position to start next search (Back track #NearbyLineRange)

	ProcedureReturn (line+#NearbyLineRange) % #NearbyEndOfLine

EndProcedure
Procedure IndexScan(line,pos)

	; line	line where text has been found
	; pos	0 = start of 'first' line in list (line-#NearbyLineRange)
	; 		1 = start of 'second' line in list (line-#NearbyLineRange+1)
	; 		:
	; 		n = start of line with search text (n=#NearbyLineRange)

	If line+pos<#NearbyEndOfLine
		ProcedureReturn #Null
	EndIf

	If pos<#NearbyEndOfLine
		;Debug "~"+Str(line)+"/"+Str(pos)+" = "+Str((line+pos) % #NearbyEndOfLine)
		ProcedureReturn (line+pos) % #NearbyEndOfLine
	Else
		ProcedureReturn #NearbyEndOfLine
	EndIf

EndProcedure
Procedure ShowLines(start,stops,limit)

	Protected c,n
	Protected Dim CRs(#NearbyAllLines)

	CRs(#NearbyLineRange)=start
	CRs(#NearbyEndOfLine)=stops

	n=#NearbyLineRange
	Repeat
		n-1
		Repeat
			start-#CharByte
		Until start<limit Or PeekC(start)=#CR
		CRs(n)=start
	Until n=0

	n=#NearbyEndOfLine
	Repeat
		n+1
		If PeekC(stops)
			Repeat
				stops+#CharByte
				c=PeekC(stops)
			Until c=#CR Or c=#Null
		EndIf
		CRs(n)=stops
	Until n=#NearbyAllLines

	For n=0 To #NearbyAllLines-1
		start=CRs(n)
		stops=Crs(n+1)
		; Debug Str(start)+"..."+Str(stops)
		Debug Chr('['-31*Bool(n=#NearbyLineRange))+ReplaceString(PeekS(start+#CharByte,(stops-start)>>#CharShft-#True),#LF$,"")+Chr(']'-31*Bool(n=#NearbyLineRange))
	Next n


EndProcedure
Procedure Search(*Search.SearchType,IgnoreCase)

	Protected *Memory.Character=*Search\Memory
	Protected *MemoryAhead.Character
	Protected *SearchAhead.Character=@*Search\Keyword

	Protected m.i,n.i
	Protected s.i=*SearchAhead\c

	; Debug "Search from line "+*Search\Line

	With *Search

		\CRs[IndexLine(\Line)]=*Memory-#CharByte

		Repeat
			m=*Memory\c
			If IgnoreCase
				m=NoCase(m)
			EndIf
			If m
				If m=#CR
					; Debug Str(\Line)+" > "+Str(\Line%#NearbyEndOfLine)+" = "+Str(*Memory)
					\CRs[\Line%#NearbyEndOfLine]=*Memory
					\Line+1
					;Debug PeekS(*Memory+#CharSize<<1,10)
				ElseIf m=s
					*MemoryAhead=*Memory
					Repeat
						*MemoryAhead+#CharByte
						*SearchAhead+#CharByte
						m=*MemoryAhead\c
						n=*SearchAhead\c
						If IgnoreCase
							m=NoCase(m)
						EndIf
					Until m<>n Or n=#Null
					If n=#Null
						;\Offset=(*MemoryAhead-*Memory)>>#CharShft
						While *MemoryAhead\c And *MemoryAhead\c<>#CR
							*MemoryAhead+#CharByte
						Wend
						\CRs[#NearbyEndOfLine]=*MemoryAhead
						\State=#SearchSuccessful+Bool(*MemoryAhead\c=#Null)
						\LastFinding=\Line
						ProcedureReturn #True
					EndIf
					*SearchAhead.Character=@\Keyword
				EndIf

			Else
				\State=#Null
				ProcedureReturn #Null
			EndIf
			*Memory+#CharByte
		ForEver

	EndWith

EndProcedure
Procedure Demo()

	Protected.i i,n,z
	Protected.i time
	Protected.i ignorecase=1
	Protected.i loops,found
	Protected.i start,stops
	Protected.s string,fake,line

	For i=0 To #CharList
		NoCase(i)=Asc(LCase(Chr(i)))
	Next i

	string=ReplaceString("This is one test.Here's a simple near.And another near.A near with 'xxx'.A near with 'hemain',to be found.and another one.agaIN one line.an inserted line.Okay, one more near.And a last main.",".",#CRLF$+"*     ")
	;string=LCase(String);					just for testing to ignore case issues for the moment...
	For i=1 To 10
		String+String
	Next i
	String="*     "+string
	n=CountString(String,"*")
	For i=1 To n
		ReplaceString(string,"*",RSet(Str(i),5),#PB_String_InPlace|#PB_String_CaseSensitive,z,1)
		z+25; speed up line numbers
	Next i
	string+"the absolute last line!"
	Debug "String has "+Str(Len(String))+" characters in "+Str(n)+" lines:"
	Debug "--------------------------------------------"
	Debug Left(string,165)+"..."+Right(string,150)
	Debug "--------------------------------------------"


	; ---------------------------------------------------------------------------------------------------------------

	MainSearch\Keyword="okay"
	MainSearch\Keyword="he"
	MainSearch\Keyword="xxx"
	;MainSearch\Keyword=" near"
	;MainSearch\Keyword="simple"
	;MainSearch\Keyword="last"
	;MainSearch\Keyword="abs"
	;MainSearch\Keyword="hem"
	MainSearch\Line=1
	MainSearch\Memory=@String

	NearSearch\Keyword="okays"
	NearSearch\Keyword="another one"
	NearSearch\Keyword="aGain"
	;NearSearch\Keyword="insert"
	;NearSearch\Keyword="this"
	;NearSearch\Keyword="absol"
	NearSearch\Line=1

	Debug "Searching for '"+MainSearch\Keyword+"' and '"+NearSearch\Keyword+"'..."
	Debug "--------------------------------------------"


	; ---------------------------------------------------------------------------------------------------------------

	line.s="10236"
	fake.s=line+" *"+MainSearch\Keyword+"*"
	CopyMemory(@fake,@String+FindString(string,line)<<#CharShft-#CharByte,Len(fake)<<#CharShft)

	; ---------------------------------------------------------------------------------------------------------------

	time=ElapsedMilliseconds()

	found=0
	loops=0
	If ignorecase
		MainSearch\Keyword=LCase(MainSearch\Keyword)
		NearSearch\Keyword=LCase(NearSearch\Keyword)
	EndIf

	Repeat

		With MainSearch
			If loops
				\Line=Max(NearSearch\Line-#NearbyLineRange,1)
				\Memory=NearSearch\CRs[IndexBack(NearSearch\Line)]+#CharByte
				; Debug "Next search starts at... "+ReplaceString(PeekS(\Memory,6),#LF$,"#")
			EndIf

			If Search(MainSearch,ignorecase)=#Null
				Break
			EndIf

			If 0
				Debug "..........................................."
				Debug "Main text found at line: "+\Line
				For i=0 To #NearbyLineRange
					start=IndexScan(\Line,i)
					stops=IndexScan(\Line,i+1)
					If start=stops
						Debug "[nil]"
					Else
						Debug "["+ReplaceString(PeekS(\CRs[start]+#CharByte,(\CRs[stops]-\CRs[start])>>#CharShft-#True),#LF$,"")+"]"
					EndIf
				Next i
				Debug "'''''''''''''''''''''''''''''''''''''''''''"
			EndIf

			If loops And \Line<=NearSearch\Line+#NearbyLineRange
				found=#True
				Break
			EndIf

		EndWith

		With NearSearch
			\Line=Max(MainSearch\Line-#NearbyLineRange,1)
			\Memory=MainSearch\CRs[IndexBack(MainSearch\Line)]+#CharByte
			; Debug "Next search starts at... "+ReplaceString(PeekS(\Memory,6),#LF$,"#")
			If Search(NearSearch,ignorecase)=#Null
				Break
			EndIf

			If 0
				Debug "..........................................."
				Debug "Nearby text found at line: "+\Line
				For i=0 To #NearbyLineRange
					start=IndexScan(\Line,i)
					stops=IndexScan(\Line,i+1)
					If start=stops
						Debug "[nil]"
					Else
						Debug "["+ReplaceString(PeekS(\CRs[start]+#CharByte,(\CRs[stops]-\CRs[start])>>#CharShft-#True),#LF$,"")+"]"
					EndIf
				Next i
				Debug "'''''''''''''''''''''''''''''''''''''''''''"
			EndIf

			If \Line<=MainSearch\Line+#NearbyLineRange
				found=#True
				Break
			EndIf

			loops+1

		EndWith

	ForEver

	If found
		Debug "Main Line: "+MainSearch\Line
		Debug "Near Line: "+NearSearch\Line
		ShowLines(MainSearch\CRs[IndexLine(MainSearch\Line)],MainSearch\CRs[#NearbyEndOfLine],@String)
	Else
		Debug "Main last found: "+Str(MainSearch\LastFinding)
		Debug "Near last found: "+Str(NearSearch\LastFinding)
	EndIf

	fake=Str(ElapsedMilliseconds()-time)+"ms"
	If #PB_Compiler_Debugger
		Debug fake
	Else
		MessageRequester(":)",fake)
	EndIf

EndProcedure
Demo()
Post Reply