by MarcoBoschi » Tue Aug 03, 2010 10:49 am
Everything comes from the fact that a function like this at_search() free searches in a table is very much faster than traditional methods.
The function returns an array containing the records that I find the string passed
as parameter.
I put this function in a application that run in a server (search32.exe) in polling mode.
Result of every request from clients is an index creation or an Array containing matching record.
#include "fivewin.ch"
#include "FileIO.ch"
ANNOUNCE RDDSYS
#define crlf CHR(13)+CHR(10)
// parameters: dbf file to open and string to search with .AND. clause ( not words but strings! )
FUNCTION MAIN()
LOCAL aRecord := {}
LOCAL i
LOCAL cRecords := ""
LOCAL nRecord
aRecord := at_search( "clifor", "COMPUTER", "PADOVA" )
FOR i := 1 TO LEN( aRecord )
cRecords := cRecords + " " + ALLTRIM( STR(aRecord[i]) )
NEXT i
? cRecords
USE clifor
INDEX ON recno() TAG COD_CLI CUSTOM TO clifor_rec
FOR EACH nRecord In aRecord
GOTO nRecord
OrdKeyAdd()
NEXT
// open browse to monitor
GO TOP
browse()
RETURN NIL
FUNCTION AT_SEARCH( cFile , cString1 , cString2, cString3, cString4 )
LOCAL iW1, iW2
LOCAL cStringa
LOCAL cIndRec := cFile + "_rec"
LOCAL nHandle
LOCAL nSize
LOCAL nRead
LOCAL nPosRec
LOCAL nHead, nRecSize
LOCAL nPosStart := 0
LOCAL nPosRecord := 0
LOCAL aPosRec1 := {}
LOCAL aPosRec2 := {}
LOCAL aString := {}
LOCAL nAndOr := 1
LOCAL aRecord := {}
LOCAL cTrova := ""
LOCAL nFound
LOCAL cRecord
LOCAL nPos2
LOCAL nIni := SECONDS()
LOCAL lDeleted
SET DELETED OFF
SET EXCLUSIVE OFF
// load an array with strings to search
IF cString1 <> NIL ; AADD( aString , cString1 ) ; ENDIF
IF cString2 <> NIL ; AADD( aString , cString2 ) ; ENDIF
IF cString3 <> NIL ; AADD( aString , cString3 ) ; ENDIF
IF cString4 <> NIL ; AADD( aString , cString4 ) ; ENDIF
// open and read all dbf file into cStringa variable
nHandle := FOPEN( cFile + ".dbf" )
nSize := FSeek( nHandle, 0, FS_END )
FSeek( nHandle, -nSize, FS_RELATIVE )
cStringa := SPACE( nSize )
nRead := FRead( nHandle, @cStringa, nSize )
FCLOSE( nHandle )
// open with use commands to obtain header and recsize
USE &cFile
nHead := header()
nRecSize := recsize()
USE
nPosRec := 1
nFound := 0
iW1 := 1
iW2 := 1
nPosStart := 1
// enter in a loop to begin search operations
DO WHILE .T.
// search counter
nFound := 0
// aPosrec1 contain in position 1 the absolute position in cStringa and in position
// 2 the calculated record
aPosRec1 := ATREC( aString[ 1 ] , cStringa , nPosStart, nHead, nRecSize )
IF aPosRec1[ 1 ] > 0 // if the first string to search is founded...
// if a * is present record is deleted
lDeleted := IIF(SUBSTR( cStringa , nHead + nRecSize * ( aPosrec1[ 2 ] -1) +1 , 1 ) = "*", .T., .F. )
IF .NOT. lDeleted
// start position for next search is the beginning of record
nPosStart := aPosRec1[1] + 1
// IF there are other strings to search
IF LEN( aString ) > 1
// first string has been founded
nFound ++
// create a string containing only the actual record
nPosRecord := nHead + nRecSize * ( aPosrec1[ 2 ] - 1 ) + 2
cRecord := SUBSTR( cStringa , nPosRecord , nRecSize )
// pointer for second string to search
iW2 := 2
DO WHILE .T.
// I search the second string only in the record string (more little)
nPos2 := AT( aString[ iW2 ] , cRecord )
IF nPos2 > 0
// second string is founded
nFound ++
ELSE
// if not found exit from loop
EXIT
ENDIF
// if there are other strings to search increment pointer of 1
IF iW2 < LEN( aString )
iW2 ++
ELSE
// otherwise exit
EXIT
ENDIF
ENDDO
// at the exit of loop IF all strings are founded I add record number to aRecord Array
IF nFound = LEN( aString )
AADD( aRecord , aPosRec1[2] )
ENDIF
nPosStart ++
ELSE // there is only one string to search
AADD( aRecord , aPosRec1[2] )
ENDIF
ELSE
nPosStart := nPosStart + nHead + nRecSize
ENDIF
ELSE
EXIT
ENDIF
ENDDO
? SECONDS() - nIni
RETURN aRecord
FUNCTION ATREC( cTesto , cStringa , nStart, nHead, nRecSize )
LOCAL nPos := 0
LOCAL nRecord := 0
nPos := AT( cTesto , cStringa , nStart )
IF nPos > 0
nRecord := INT( ( nPos - nHead ) / nRecSize ) + 1
ENDIF
RETURN { nPos, nRecord }
FUNCTION DbfSize()
RETURN ( (RecSize() * LastRec()) + Header() + 1 )
INIT PROCEDURE RddInit
REQUEST DBFFPT
REQUEST DBFCDX
rddSetDefault( "DBFCDX" )
RETURN