Hi all,
Here, at last, is my REPLACE clone. I've dubbed it FN PLUNGER, because it is versatile and powerful like FN MUNGER, but it doesn't stop until the job is done. And in PPC with register on, it should be significantly faster!
FN PLUNGER calls FN FindAll, which is based on my version of the super-fast Boyer-Moore-Harspool search algorithm that found :) its way onto this list several months ago. FN FindAll creates a global DYNAMIC array of offsets to each place the target is found. This array could be used for other purposes, as well. (Did someone say "Undo?")
I have combined these 2 FNs (and another supporting FN) into an include that can be dropped into any project. Because the params of FN PLUNGER and FN FindAll, like those of FN MUNGER, can be a bit daunting, I've also included with it, a set of wrapper FNs to help you use a container, handle, or string for the the text to be searched, and either a string or a text pointer for the target and replacement:
The first set simply delete the target from the text:
numChnged = fn zapStrFromCtnr(theStr$,theContainer) //Not case-sensitive
numChnged = fn zapStrFromCtnrSens(theStr$,theContainer) //Case-sensitive
numChnged = fn zapStrFromHndl(theStr$,theHandle&) //Not case-sensitive
numChnged = fn zapStrFromHndlSens(theStr$,theHandle&) //Case-sensitive
numChnged = fn zapStrFromStr(strToZap$,strToSrch$) // etc.
numChnged = fn zapStrFromStrSens(strToZap$,strToSrch$) // etc.
numChnged = fn zapTxtFromCtnr(txtPtr&,txtLen,theContainer)
numChnged = fn zapTxtFromCtnrSens(txtPtr&,txtLen,theContainer)
numChnged = fn zapTxtFromHndl(txtPtr&,txtLen,theHandle&)
numChnged = fn zapTxtFromHndlSens(txtPtr&,txtLen,theHandle&)
The rest replace the target with substitute text:
numChnged = fn replStrInCtnr(tgtStr$,theContainer)
numChnged = fn replStrInCtnrSens(tgtStr$,theContainer)
numChnged = fn replStrInHndl(tgtStr$,subStr$,theHandle&)
numChnged = fn replStrInHndlSens(tgtStr$,subStr$,theHandle&)
numChnged = fn replStrInStr(tgtStr$,subStr$,strToSrch$)
numChnged = fn replStrInStrSens(tgtStr$,subStr$,strToSrch$)
numChnged = fn replTxtInCtnr(tgtPtr&,tgtLen&,subPtr&,subLen&,theContnr)
numChnged = fn replTxtInCtnrSens(tgtPtr&,tgtLen&,subPtr&,subLen&,theContnr)
numChnged = fn replTxtInHndl(tgtPtr&,tgtLen&,subPtr&,subLen&,theHndl&)
numChnged = fn replTxtInHndlSens(tgtPtr&,tgtLen&,subPtr&,subLen&,theHndl&)
This may seem overkill, but it should allow you to work easily with data in any form or size. Please note these wrappers have not all been tested.
Here's the include, followed by a brief, timed demo showing a larger replacement, a same-size replacement, a smaller replacement, and a deletion. If your emailer has too much trouble with my line-break chars, just ask off list and I'll send you the files.
Enjoy!
0"0
=J= a y
"
'~FN PLUNGER incl
goto "End FN PLUNGER incl"
'~'6
begin globals
dim gFNFindAllOppStr as str255
dynamic gFNFindAllList(_maxLong) as long
end globals
'~'6
local fn initgOppStr
dim r
for r = 1 to 255
gFNFindAllOppStr[r] = r
next
gFNFindAllOppStr[0] = 255
fn lowercasetext(@gFNFindAllOppStr+1,255,0)
for r = 65 to 255
if gFNFindAllOppStr[r] <> r then gFNFindAllOppStr[gFNFindAllOppStr[r]] = r
next
end fn
'~'6
local
dim as ptr k, textPos, targetPos, count ' Register Vars
dim as long targetStart, targetTop, txtEndPtr, tempChar ' Register Vars
dim as ptr txtStartPtr' RAM Var
dim as str255 skip, key'strVar[x] is faster than arrays
local fn FindAll( textH as handle, target as ptr, targetLen&, isSensitive)
/*
// Search for target in the text contained in textH.
// Returns number of instances found, and puts the
// offsets into the dynamic array gFNFindAllList().
// Optional case-insensitive version by
// Jay A. Reeve, October, 2001
// Based on Robert Purves's better optimisation
// of Mark's BoyerMooreHarspool routine
*/
'Initialize vars
if targetLen < 1 then exit fn' invalid target string
target --' make 1-based array by adding 0 elem
txtStartPtr = [textH] - 1' make 1-based array by adding 0 elem
txtEndPtr = fn GetHandleSize( textH ) + txtStartPtr
textPos = targetLen + txtStartPtr - 2
targetStart = @key
targetTop = targetStart + targetLen - 1
count = @gFNFindAllList + _AutoXREFCurr
'Initialize list count to 0
count.nil& = 0
'Create skip lookup
def blockfill( @skip, 256, targetLen )
for k = 1 to targetLen
skip[ | target + k | ] = targetLen - k
next
'If not case-sensitive, add skip data for opposite case
long if isSensitive = _false
IF gFNFindAllOppStr[0] <> 255 then fn initgOppstr
for k = 1 to targetLen
skip[ gFNFindAllOppStr[ | target + k | ] ] = targetLen - k
next
end if
'Make target key
for k = 1 to targetLen
key[ k ] = skip[ | target + k | ]
next
'Find all instances and record their offsets
while ( textPos <= txtEndPtr )
tempChar = skip[ textPos.nil`` ]
long if tempChar
textPos += tempChar
xelse
targetPos = targetTop
k = textPos - 1
while targetPos > targetStart
if ( skip[ k.nil`` ] <> targetPos.nil`` ) then exit while
targetPos --
k --
wend
long if targetPos <= targetStart'Found one
gFNFindAllList(count.nil&) = k + 1 - [textH]
end if
textPos ++
end if
wend
'Return number found
end fn = count.nil&
'~'6
clear local
dim as long r, count, temp
local fn PLUNGER(repThis as ptr, repL&, withThis as Ptr, withL&, ¬
inThis as hndl, isSensitive)
dim as long bytes2Add
'Check for valid params
if repThis = 0 or repL = 0 or inThis = 0 then exit fn
if withL then if withThis = 0 then exit fn
'Get list of offsets to found instances
count = fn findAll( inThis, repThis, repL&, isSensitive ) - 1
if count < 0 then exit fn
'precalc difference in length
bytes2Add = withL - repL
'Mark end of text
gFNFindAllList( count + 1 ) = fn gethandlesize( inThis )
select bytes2Add
case > 0
'move text higher in handle
sethandlesize(inThis, gFNFindAllList(count + 1) + bytes2Add * (count + 1))
if syserror then stop "Couldn't resize handle in FN PLUNGER"
for r = count to 0 step -1
temp = gFNFindAllList( r ) + repL
blockmove [inThis] + temp, [inThis] + temp + bytes2Add * (r + 1), ¬
gFNFindAllList(r + 1)-temp
next
case < 0
'move text lower in handle
for r = 0 to count
temp = gFNFindAllList( r ) + repL
blockmove [inThis] + temp, [inThis] + temp + bytes2Add * (r + 1), ¬
gFNFindAllList(r + 1)-temp
next
sethandlesize(inThis, gFNFindAllList(r) + bytes2Add * r)
end select
long if withL
'copy replacement string into handle
temp = [inThis]
for r = 0 to count
blockmove withThis, temp + gFNFindAllList(r), withL
temp += bytes2Add
next
end if
end fn = count + 1
'~'6
'~Wrappers for FN PLUNGER
'~'6
def fn zapStrFromCtnr(theStr$,@theCtnr as ptr) ¬
= fn PLUNGER(@theStr[1],theStr[0],0,0,[theCtnr],_false)
'~'6
def fn zapStrFromCtnrSens(theStr$,@theCtnr as ptr) ¬
= fn PLUNGER(@theStr[1],theStr[0],0,0,[theCtnr],_zTrue)
'~'6
def fn zapStrFromHndl(theStr$,theH&) ¬
= fn PLUNGER(@theStr[1],theStr[0],0,0,theH&,_false)
'~'6
def fn zapStrFromHndlSens(theStr$,theH&) ¬
= fn PLUNGER(@theStr[1],theStr[0],0,0,theH&,_zTrue)
'~'6
def fn zapTxtFromCtnr(txtP as ptr,txtLen,@theCtnr as ptr) ¬
= fn PLUNGER(txtP,txtLen,0,0,[theCtnr],_false)
'~'6
def fn zapTxtFromCtnrSens(txtP as ptr,txtLen,@theCtnr as ptr) ¬
= fn PLUNGER(txtP,txtLen,0,0,[theCtnr],_zTrue)
'~'6
def fn zapTxtFromHndl(txtP as ptr,txtLen,theH&) ¬
= fn PLUNGER(txtP,txtLen,0,0,theH&,_false)
'~'6
def fn zapTxtFromHndlSens(txtP as ptr,txtLen,theH&) ¬
= fn PLUNGER(txtP,txtLen,0,0,theH&,_zTrue)
'~'6
local mode
dim H as hndl,count,L
local fn zapStrFromStr(@zap as ptr,@strP as ptr)
L = strP.0``
H = fn newhandle(L)
blockmove strP+1,[H],L
count = fn zapStrFromHndl(zap.0$,H)
L -= count * zap.0``
blockmove [H],strP+1,L
strP.0`` = L
disposehandle(H)
end fn = count
'~'6
local mode
dim H as hndl,count,L
local fn zapStrFromStrSens(@zap as ptr,@strP as ptr)
L = strP.0``
H = fn newhandle(L)
blockmove strP+1,[H],L
count = fn zapStrFromHndlSens(zap.0$,H)
L -= count * zap.0``
blockmove [H],strP+1,L
strP.0`` = L
disposehandle(H)
end fn = count
'~'6
def fn replStrInCtnr(theStr$,subStr$,theCtnr as ptr) ¬
= fn PLUNGER(@theStr[1],theStr[0],@subStr[1],subStr[0],[theCtnr],_false)
'~'6
def fn replStrInCtnrSens(theStr$,subStr$,@theCtnr as ptr) ¬
= fn PLUNGER(@theStr[1],theStr[0],@subStr[1],subStr[0],[theCtnr],_zTrue)
'~'6
def fn replStrInHndl(theStr$,subStr$,theH&) ¬
= fn PLUNGER(@theStr[1],theStr[0],@subStr[1],subStr[0],theH&,_false)
'~'6
def fn replStrInHndlSens(theStr$,subStr$,theH&) ¬
= fn PLUNGER(@theStr[1],theStr[0],@subStr[1],subStr[0],theH&,_zTrue)
'~'6
def fn replTxtInCtnr(txtP as ptr,txtLen&,subP as ptr,subLen&,@theCtnr as ptr) ¬
= fn PLUNGER(txtP,txtLen,subP,subLen,[theCtnr],_false)
'~'6
def fn replTxtInCtnrSens(txtP as ptr,txtLen&,subP as ptr,subLen&,¬
@theCtnr as ptr) = fn PLUNGER(txtP,txtLen,subP,subLen,[theCtnr],_zTrue)
'~'6
def fn replTxtInHndl(txtP as ptr,txtLen&,subP as ptr,subLen&,theH&) ¬
= fn PLUNGER(txtP,txtLen,subP,subLen,theH&,_false)
'~'6
def fn replTxtInHndlSens(txtP as ptr,txtLen&,subP as ptr,subLen&,theH&) ¬
= fn PLUNGER(txtP,txtLen,subP,subLen,theH&,_zTrue)
'~'6
local mode
dim H as hndl,count,L
local fn replStrInStr(@tgtP as ptr,@subP as ptr,@strP as ptr)
L = strP.0``
H = fn newhandle(L)
if H = 0 then count = H : Exit fn
blockmove strP+1,[H],L
count = fn replStrInHndl(tgtP.0$,subP.0$,H)
L += count * (subP.0`` - tgtP.0``)
if L > 255 then L = 255
blockmove [H],strP+1,L
strP.0`` = L
disposehandle(H)
end fn = count
'~'6
local mode
dim H as hndl,count,L
local fn replStrInStrSens(@tgtP as ptr,@subP as ptr,@strP as ptr)
L = strP.0``
H = fn newhandle(L)
if H = 0 then count = H : Exit fn
blockmove strP+1,[H],L
count = fn replStrInHndlSens(tgtP.0$,subP.0$,H)
L += count * (subP.0`` - tgtP.0``)
if L > 255 then L = 255
blockmove [H],strP+1,L
strP.0`` = L
disposehandle(H)
end fn = count
'~'6
"End FN PLUNGER incl"
//======================
"PLUNGER DEMO"
'RUN IN CONSOLE
'~'6
/*
MICROSECOND TIMER
By Robert Purves
*/
begin record MicroSecRecord
dim as unsigned long microSecHi, microSecLo
end record
Toolbox Microseconds( long ) `0xA193,0x225F,0x22C8,0x2280
local fn DeltaMilliSeconds$( oldMS as ptr to MicroSecRecord )
// difference between old microseconds and present; convert to milliseconds
dim nowMS as MicroSecRecord
fn Microseconds( nowMS )
end fn = using "#####.#"; (nowMS.microSecLo - oldMS.microSecLo)/1000.0
'~'6
begin globals
dim M as MicroSecRecord
dim c$$,tgt$,repl$,n&`,i
end globals
dim system 20000000// MAJOR MEMORY HOG!
local fn showResults(tt$,rr$,nn)
DIM temp$
temp = fn DeltaMilliSeconds$( M )
print """";tt;"""";" replaced with ";"""";repl;""""
print nn;" times in";temp;" milliseconds."
print left$$(c,72);"... ";right$$(c,72)
print
end fn
c = "Sus percaleafs rage! Oxpeas alidocs eaount! "
print
print "€ Searching a";len(c)*100000;"-char container comprising 100000"
print "€ repetitions of ";"""";c;""""
print
for i = 1 to 5
c += c + c + c + c + c + c + c + c + c
next
'
tgt = "! o"
repl = "alinttice"
fn Microseconds( M )
n = FN replStrInCtnr(tgt,repl,c)
fn showResults(tgt,repl,n)
'
tgt = "nt"
repl = "ss "
fn Microseconds( M )
n = FN replStrInCtnr(tgt,repl,c)
fn showResults(tgt,repl,n)
'
tgt = "ea"
repl = "i"
fn Microseconds( M )
n = FN replStrInCtnr(tgt,repl,c)
fn showResults(tgt,repl,n)
'
tgt = "s "
repl = ""
fn Microseconds( M )
n = Fn zapStrFromCtnr(tgt,c)
fn showResults(tgt,repl,n)
'~'6
"End PLUNGER DEMO"