I've been working on this function for a few weeks now and I just can't get it right, it's worked for some of the strings I wanted to replace (converting a DBC project) but I'm being quite demanding of it and it's getting complicated.
The function must have four key features:
* It must search for multiple instances of the find string within the source string.
* It reads "a~b" as accepting anything in between a and b, like most search function use "*" but "~" is an easier symbol to use since it is not in DB code.
* It must also read "~" in the replace string so that you can replace only the search terms either side of "~" but preserve the text in between, eg: source = "mary had a little lamb.", find = "mary~lamb", replace = "dexter~lab" becomes "dexter had a little lab."
* Finally, if matching brackets are in the search terms they must be preserved, i.e. source = "((()))" find = "(~)" should take "((()))" NOT "((()"
Here's my semi-broken code:
#constant openBracket = chr$(40)
#constant closeBracket = chr$(41)
remstart
open to read 1,"space conflict 2.dba"
fn$ = "output.dba"
if file exist(fn$) then delete file fn$
open to write 2,fn$
write string 2,"// FILE GENERATED: "+get time$()+" "+get date$()
remend
remstart
data "spoon(~,1)", "race(~).att"
data "spoon(~,2)", "race(~).def"
data "spoon(~,3)", "race(~).pgr"
data "spoon(~,4)", "race(~).economy"
data "spoon(~,5)", "race(~).colour"
data "spoon(~,6)", "race(~).desiredColonies"
data "spoon(~,7)", "race(~).engageDistance"
data "fork(~,1)", "shipClass(~).att"
data "fork(~,2)", "shipClass(~).def"
data "fork(~,3)", "shipClass(~).speed"
data "fork(~,4)", "shipClass(~).range"
data "fork(~,5)", "shipClass(~).cost"
data "fork(~,6)", "shipClass(~).crew"
remend
max = 1
type search
find as string
replace as string
endtype
dim str(max) as search
for i = 1 to max
read str(i).find
read str(i).replace
print str(i).find
print str(i).replace
next i
sync on
sync rate 0
sync
print
data "spoon(~,1)","fork(~).bar" //these get read above
data "spoon(223,1)","spoon(knife(1))","spoon(2,1) spoon(1,2) spoon(3,1)"
for i = 0 to 0
read a$
print a$
print
print UberReplace$(a$,str(1).find,str(1).replace,0)
next i
sync
wait key
remstart
change = 0
while file end(1)=0
read string 1,a$
for i = 1 to max
b$ = uberReplace$(a$,str(i).find,str(i).replace,open)
if a$ <> b$
inc change
a$ = b$
cls
print change;" changes..."
sync
endif
next i
write string 2,a$
endwhile
close file 1
close file 2
remend
end
rem --- Functions ---------------------------------------------------------------------------------
function uberReplace$(source$,find$,replace$, open)
print source$,", ", find$,", ", replace$,", ", open:sync
rem locate split-search character in f
fTilde = chrPos(find$,"~",0)
print fTilde:sync
rem split-search found
if fTilde > 0
`split the search terms around the split character
fPre$ = left$(find$,fTilde-1)
fAft$ = right$(find$,len(find$)-fTilde)
`validate bracket pairing in search string
fPreOpen = openBrackets(fPre$,open)
fAftOpen = openBrackets(fAft$,open)
if fPreOpen > abs(fAftOpen) then fPreOpen = abs(fAftOpen) //only consider paired brackets
rem If r is split it means we will keep the text in between search terms... later.
rTilde = chrPos(replace$,"~",0)
if rTilde > 0
rPre$ = left$(replace$,rTilde-1)
rAft$ = right$(replace$,len(replace$)-rTilde)
endif
`locate first instance of fPre$ in s
sPre$ = split$(source$,fPre$,0,0)
rem Analyse every instance of fPre$ in s
for i = 0 to i + (sPre$ <> source$) - (sPre$ = source$)
sAft$ = right$(source$,len(source$)-len(sPre$))
sMid$ = split$(sAft$,fAft$,0,0)
for j = 0 to j + (sMid$ <> sAft$) - (sMid = sAft)
sAftSub$ = right$(sAft$,len(sAft$)-len(sMid$))
sRet$ = uberReplace$(sAftSub$,fAft$,rAft$,fPreOpen)
if sRet$ <> sAft$
if rTilde > 0
source$ = sPre$ + rPre$ + sMid$ + sRet$
else
source$ = sPre$ + replace$ + sRet$
endif
endif
sMid$ = split$(sAft$,fAft$,0,j)
next j
`locate next instance of fPre$ in s
sPre$ = split$(source$,fPre$,0,i)
next i
//All instances of fPre$ in s have been analysed.
else
rem bracketed replace
if open > 0
sAft$ = source$
close = CloseBracketPos(sAft$,open)
for i = 0 to i + (close>0) - (close=0)
sMid$ = left$(sAft$,close)
sAft$ = right$(sAft$,len(source$)-close)
sRet$ = uberReplace$(sMid$,find$,replace$,open-1)
if sRet$ <> sMid$ then sMid$ = sRet$
sPre$ = sPre$ + sMid$
close = CloseBracketPos(sAft$,open)
next i
else
for i = 0 to i + (sPre$ <> source$) - (sPre$=source$)
sPre$ = split$(source$,find$,0,0)
sAft$ = split$(source$,find$,1,0)
if sPre$ <> source$ then source$ = sPre$ + replace$ + sAft$
next i
endif
endif
endfunction source$
//
function CloseBracketPos(main as string,open)
for x = 1 to len(main)
select mid$(main,x)
case "(" : inc open : endcase
case ")" : dec open : endcase
endselect
if open = 0 then exitfunction x
next x
endfunction 0
//
function openBrackets(s$,open)
for i = 1 to len(s$)
if mid$(s$,i) = openBracket then inc open
if mid$(s$,i) = closeBracket then dec open
next i
endfunction open
//
rem Returns a section of the source$ string starting at 'pos' and extending 'wid' characters.
function midleft$(a$,pos,wid)
a$ = right$(left$(a$,pos+wid),wid)
endfunction a$
//
rem Returns the left- or right-most part of a string split at 'find$'.
rem 'part' indicates whether the left (0) or right (0) of the split should be returned.
rem 'instance' indicates which instance of find$ to split at.
rem 'find$' is omitted from the returned string.
function split$(s$,f$,part,instance)
part = part + instance + instance
for x = 0 to len(s$)-len(f$)
if midleft$(lower$(s$),x,len(f$)) = lower$(f$)
select part
case 0 : exitfunction left$(s$,x) : endcase
case 1 : exitfunction right$(s$,len(s$)-len(f$)-x) : endcase
case default : s$ = midleft$(s$,x+1,len(s$)) : dec part,2 : endcase
endselect
endif
next x
endfunction s$
//
rem Returns the position of instance i of character c$ within source$$.
function chrPos(source$$,c$,i)
for x = 1 to len(source$$)
if mid$(source$$,x) = c$
if i=0 then exitfunction x else dec i
endif
next x
endfunction 0
//
