|
code
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Replace one occurence of string within string at certain positionanother string and it looks the only way is with CopyMemory: Private Declare Sub CopyMemory _ Lib "kernel32" _ Alias "RtlMoveMemory" _ (pDst As Any, _ pSrc As Any, _ ByVal ByteLen As Long) Sub ReplaceAt(lStringPtr As Long, _ lReplacePtr As Long, _ lReplacePos As Long, _ lReplaceLenB As Long) 'lStringPtr will the StrPtr of the string to do the replace in 'lReplacePtr will the StrPtr of the string to do the replace with 'lReplacePos will be the string position (as in Mid$ etc.) to do the replace 'lReplaceLenB will be LenB of strReplace '---------------------------------------------------------------------------- Dim lMemPos As Long lMemPos = (lStringPtr + lReplacePos * 2) - 2 CopyMemory ByVal lMemPos, _ ByVal lReplacePtr, _ lReplaceLenB End Sub The only other way I can see is with concatenation, so do Left$( etc. & strReplace & Right$ etc., but that will be slow. The method with CopyMemory seems to work fine, but ideally I would like to avoid it as the error handling will be more tricky. Is there a way to do this fast without CopyMemory? RBS "RB Smissaert" <bartsmissa***@blueyonder.co.uk> wrote in message Unless I've misread your question then you should be able to do what you news:OH6t2flrJHA.3864@TK2MSFTNGP02.phx.gbl... > Looking for a fast way to replace at a given position a > string within another string and it looks the only way is > with CopyMemory: want using the native VB Mid statement, as in the following example: Dim s1 As String, s2 As String s1 = "1234567890123456789012345678901234567890" s2 = "ABCDE" Print s1 Mid$(s1, 20) = s2 Print s1 There is a third optional parameter, which I have not used above, enabling you to specify how many characters to replace, otherwise the whole of s2 will be used. Post again if you meant something different. Mike Yes, thanks, you are absolutely right.
I thought I had a look at that a while ago and decided it couldn't work, but it clearly does. Even better, the simple Mid$ is in fact faster by a factor 4 in my test. RBS Show quoteHide quote "Michael Williams" <M***@WhiskeyAndCoke.com> wrote in message news:uFyDEslrJHA.3848@TK2MSFTNGP02.phx.gbl... > "RB Smissaert" <bartsmissa***@blueyonder.co.uk> wrote in message > news:OH6t2flrJHA.3864@TK2MSFTNGP02.phx.gbl... > >> Looking for a fast way to replace at a given position a >> string within another string and it looks the only way is >> with CopyMemory: > > Unless I've misread your question then you should be able to do what you > want using the native VB Mid statement, as in the following example: > > Dim s1 As String, s2 As String > s1 = "1234567890123456789012345678901234567890" > s2 = "ABCDE" > Print s1 > Mid$(s1, 20) = s2 > Print s1 > > There is a third optional parameter, which I have not used above, enabling > you to specify how many characters to replace, otherwise the whole of s2 > will be used. Post again if you meant something different. > > Mike > > I know now why I thought this couldn't work.
Previously I was interested in inserting a string into another string, so not replacing existing text. In that case the Mid$ function can't work and I think you will need to concatenate. RBS Show quoteHide quote "Michael Williams" <M***@WhiskeyAndCoke.com> wrote in message news:uFyDEslrJHA.3848@TK2MSFTNGP02.phx.gbl... > "RB Smissaert" <bartsmissa***@blueyonder.co.uk> wrote in message > news:OH6t2flrJHA.3864@TK2MSFTNGP02.phx.gbl... > >> Looking for a fast way to replace at a given position a >> string within another string and it looks the only way is >> with CopyMemory: > > Unless I've misread your question then you should be able to do what you > want using the native VB Mid statement, as in the following example: > > Dim s1 As String, s2 As String > s1 = "1234567890123456789012345678901234567890" > s2 = "ABCDE" > Print s1 > Mid$(s1, 20) = s2 > Print s1 > > There is a third optional parameter, which I have not used above, enabling > you to specify how many characters to replace, otherwise the whole of s2 > will be used. Post again if you meant something different. > > Mike > > I explained that wrongly, I meant I was thinking at a situation where the
replacing string is longer than the string to be replaced. In that case I think you will need something like this, which is slow due to the string concatenations: Private Function ReplaceAt(strToSearch, strToFind, strReplace, lPos) As String 'will replace strToFind in strToSearch with strReplace, 'but only if strToFind appears at position lPos '------------------------------------------------------ Dim strTest As String strTest = Mid$(strToSearch, lPos, Len(strToFind)) If strTest = strToFind Then ReplaceAt = Left$(strToSearch, lPos - 1) & _ strReplace & _ Right$(strToSearch, Len(strToSearch) - (lPos + (Len(strToFind) - 1))) Else ReplaceAt = strToSearch End If End Function RBS Show quoteHide quote "RB Smissaert" <bartsmissa***@blueyonder.co.uk> wrote in message news:O72am6lrJHA.1300@TK2MSFTNGP05.phx.gbl... >I know now why I thought this couldn't work. > Previously I was interested in inserting a string into another string, so > not replacing existing text. > In that case the Mid$ function can't work and I think you will need to > concatenate. > > RBS > > > "Michael Williams" <M***@WhiskeyAndCoke.com> wrote in message > news:uFyDEslrJHA.3848@TK2MSFTNGP02.phx.gbl... >> "RB Smissaert" <bartsmissa***@blueyonder.co.uk> wrote in message >> news:OH6t2flrJHA.3864@TK2MSFTNGP02.phx.gbl... >> >>> Looking for a fast way to replace at a given position a >>> string within another string and it looks the only way is >>> with CopyMemory: >> >> Unless I've misread your question then you should be able to do what you >> want using the native VB Mid statement, as in the following example: >> >> Dim s1 As String, s2 As String >> s1 = "1234567890123456789012345678901234567890" >> s2 = "ABCDE" >> Print s1 >> Mid$(s1, 20) = s2 >> Print s1 >> >> There is a third optional parameter, which I have not used above, >> enabling you to specify how many characters to replace, otherwise the >> whole of s2 will be used. Post again if you meant something different. >> >> Mike >> >> > RB Smissaert wrote:
> I explained that wrongly, I meant I was thinking at a situation where the In that case, you can use the Replace function. <g>> replacing string is longer than the string to be replaced. ?replace("1234567", "4", "abc") 123abc567 If you need a VB5 implementation, here's one I stole from somewhere: Public Function Replace(ByVal Expression As String, ByVal Find As String, ByVal Replase As String, Optional Start As Long = 1, Optional Count As Long = -1, Optional Compare As VbCompareMethod = vbBinaryCompare) As String Dim nC As Long, nPos As Long Dim nFindLen As Long, nReplaceLen As Long nFindLen = Len(Find) nReplaceLen = Len(Replase) If (Find <> "") And (Find <> Replase) Then nPos = InStr(Start, Expression, Find, Compare) Do While nPos nC = nC + 1 Expression = Left(Expression, nPos - 1) & Replase & Mid(Expression, nPos + nFindLen) If Count <> -1 And nC >= Count Then Exit Do nPos = InStr(nPos + nReplaceLen, Expression, Find, Compare) Loop End If Replace = Expression End Function Unless I am overlooking something simple again, I don't think that will
work. The thing is I need to replace at a specified position: Sub test() Dim str1 As String Dim str2 As String str1 = "123456789" str2 = "XX" str1 = Replace(str1, "4", str2, 4, 1) 'I need result to be "123XX56789" 'but will get "XX56789 MsgBox str1 End Sub RBS Show quoteHide quote "Karl E. Peterson" <k***@mvps.org> wrote in message news:eFSc1amrJHA.1212@TK2MSFTNGP04.phx.gbl... > RB Smissaert wrote: >> I explained that wrongly, I meant I was thinking at a situation where the >> replacing string is longer than the string to be replaced. > > In that case, you can use the Replace function. <g> > > ?replace("1234567", "4", "abc") > 123abc567 > > If you need a VB5 implementation, here's one I stole from somewhere: > > Public Function Replace(ByVal Expression As String, ByVal Find As > String, ByVal Replase As String, Optional Start As Long = 1, Optional > Count As Long = -1, Optional Compare As VbCompareMethod = vbBinaryCompare) > As String > Dim nC As Long, nPos As Long > Dim nFindLen As Long, nReplaceLen As Long > > nFindLen = Len(Find) > nReplaceLen = Len(Replase) > > If (Find <> "") And (Find <> Replase) Then > nPos = InStr(Start, Expression, Find, Compare) > Do While nPos > nC = nC + 1 > Expression = Left(Expression, nPos - 1) & Replase & > Mid(Expression, nPos + nFindLen) > If Count <> -1 And nC >= Count Then Exit Do > nPos = InStr(nPos + nReplaceLen, Expression, Find, Compare) > Loop > End If > > Replace = Expression > End Function > > -- > .NET: It's About Trust! > http://vfred.mvps.org > RB Smissaert wrote:
Show quoteHide quote > Unless I am overlooking something simple again, I don't think that will Well, I'm not sure what exactly you're overlooking, but when I do this in the > work. The thing is I need to replace at a specified position: > > Sub test() > Dim str1 As String > Dim str2 As String > > str1 = "123456789" > > str2 = "XX" > > str1 = Replace(str1, "4", str2, 4, 1) > > 'I need result to be "123XX56789" > 'but will get "XX56789 > MsgBox str1 > End Sub Immediate Window: ?Replace("123456789", "4", "XX", 4, 1) 123XX56789 It seems to do what you say you want it to? Maybe it's that I'm taking what you say you need too literally, and what you really need is what you allude you need? <g> IOW, you don't care what's at position four, and it may be entirely non-unique, but you need to replace it? Yeah, in that case, it's a mess. You can split and concatenate. Or you can slide stuff around with CopyMemory. Is this something you need to do repeatedly? Did you run the exact code as posted?
Yes, in this case I needed to replace at the exact position and the string to replace could be anywhere else. As you say it looks in that case you need to concatenate. RBS Show quoteHide quote "Karl E. Peterson" <k***@exmvps.org> wrote in message news:%23Xc9MCnrJHA.1504@TK2MSFTNGP03.phx.gbl... > RB Smissaert wrote: >> Unless I am overlooking something simple again, I don't think that will >> work. The thing is I need to replace at a specified position: >> >> Sub test() >> Dim str1 As String >> Dim str2 As String >> >> str1 = "123456789" >> >> str2 = "XX" >> >> str1 = Replace(str1, "4", str2, 4, 1) >> >> 'I need result to be "123XX56789" >> 'but will get "XX56789 >> MsgBox str1 >> End Sub > > Well, I'm not sure what exactly you're overlooking, but when I do this in > the Immediate Window: > > ?Replace("123456789", "4", "XX", 4, 1) > 123XX56789 > > It seems to do what you say you want it to? Maybe it's that I'm taking > what you say you need too literally, and what you really need is what you > allude you need? <g> IOW, you don't care what's at position four, and it > may be entirely non-unique, but you need to replace it? Yeah, in that > case, it's a mess. You can split and concatenate. Or you can slide stuff > around with CopyMemory. Is this something you need to do repeatedly? > -- > .NET: It's About Trust! > http://vfred.mvps.org > Let's combine some of the techniques (VB6 version)...
Function Substitute(FullText As String, Position As Long, _ Length As Long, NewText As String) As String Substitute = FullText Mid(Substitute, Position) = String(Length, Chr$(1)) Substitute = Replace(Substitute, String(Length, Chr$(1)), NewText) End Function Of course, a final version should probably have some kind of error testing I suppose. -- Show quoteHide quoteRick (MVP - Excel) "Karl E. Peterson" <k***@exmvps.org> wrote in message news:%23Xc9MCnrJHA.1504@TK2MSFTNGP03.phx.gbl... > RB Smissaert wrote: >> Unless I am overlooking something simple again, I don't think that will >> work. The thing is I need to replace at a specified position: >> >> Sub test() >> Dim str1 As String >> Dim str2 As String >> >> str1 = "123456789" >> >> str2 = "XX" >> >> str1 = Replace(str1, "4", str2, 4, 1) >> >> 'I need result to be "123XX56789" >> 'but will get "XX56789 >> MsgBox str1 >> End Sub > > Well, I'm not sure what exactly you're overlooking, but when I do this in > the Immediate Window: > > ?Replace("123456789", "4", "XX", 4, 1) > 123XX56789 > > It seems to do what you say you want it to? Maybe it's that I'm taking > what you say you need too literally, and what you really need is what you > allude you need? <g> IOW, you don't care what's at position four, and it > may be entirely non-unique, but you need to replace it? Yeah, in that > case, it's a mess. You can split and concatenate. Or you can slide stuff > around with CopyMemory. Is this something you need to do repeatedly? > -- > .NET: It's About Trust! > http://vfred.mvps.org > Oh, I meant to add "... without using concatenation" to the opening
sentence. -- Show quoteHide quoteRick (MVP - Excel) "Rick Rothstein" <rick.newsNO.SPAM@NO.SPAMverizon.net> wrote in message news:O9l2hnorJHA.6040@TK2MSFTNGP02.phx.gbl... > Let's combine some of the techniques (VB6 version)... > > Function Substitute(FullText As String, Position As Long, _ > Length As Long, NewText As String) As String > Substitute = FullText > Mid(Substitute, Position) = String(Length, Chr$(1)) > Substitute = Replace(Substitute, String(Length, Chr$(1)), NewText) > End Function > > Of course, a final version should probably have some kind of error testing > I suppose. > > -- > Rick (MVP - Excel) > > > "Karl E. Peterson" <k***@exmvps.org> wrote in message > news:%23Xc9MCnrJHA.1504@TK2MSFTNGP03.phx.gbl... >> RB Smissaert wrote: >>> Unless I am overlooking something simple again, I don't think that will >>> work. The thing is I need to replace at a specified position: >>> >>> Sub test() >>> Dim str1 As String >>> Dim str2 As String >>> >>> str1 = "123456789" >>> >>> str2 = "XX" >>> >>> str1 = Replace(str1, "4", str2, 4, 1) >>> >>> 'I need result to be "123XX56789" >>> 'but will get "XX56789 >>> MsgBox str1 >>> End Sub >> >> Well, I'm not sure what exactly you're overlooking, but when I do this in >> the Immediate Window: >> >> ?Replace("123456789", "4", "XX", 4, 1) >> 123XX56789 >> >> It seems to do what you say you want it to? Maybe it's that I'm taking >> what you say you need too literally, and what you really need is what you >> allude you need? <g> IOW, you don't care what's at position four, and it >> may be entirely non-unique, but you need to replace it? Yeah, in that >> case, it's a mess. You can split and concatenate. Or you can slide >> stuff around with CopyMemory. Is this something you need to do >> repeatedly? >> -- >> .NET: It's About Trust! >> http://vfred.mvps.org >> > Of course, with concatenation, it is just a one-liner<g>....
Function Substitute(FullText As String, Position As Long, _ Length As Long, NewText As String) As String Substitute = Left(FullText, Position - 1) & NewText & _ Mid(FullText, Position + Length) End Function -- Show quoteHide quoteRick (MVP - Excel) "Rick Rothstein" <rick.newsNO.SPAM@NO.SPAMverizon.net> wrote in message news:uuTHCsorJHA.3648@TK2MSFTNGP05.phx.gbl... > Oh, I meant to add "... without using concatenation" to the opening > sentence. > > -- > Rick (MVP - Excel) > > > "Rick Rothstein" <rick.newsNO.SPAM@NO.SPAMverizon.net> wrote in message > news:O9l2hnorJHA.6040@TK2MSFTNGP02.phx.gbl... >> Let's combine some of the techniques (VB6 version)... >> >> Function Substitute(FullText As String, Position As Long, _ >> Length As Long, NewText As String) As String >> Substitute = FullText >> Mid(Substitute, Position) = String(Length, Chr$(1)) >> Substitute = Replace(Substitute, String(Length, Chr$(1)), NewText) >> End Function >> >> Of course, a final version should probably have some kind of error >> testing I suppose. >> >> -- >> Rick (MVP - Excel) >> >> >> "Karl E. Peterson" <k***@exmvps.org> wrote in message >> news:%23Xc9MCnrJHA.1504@TK2MSFTNGP03.phx.gbl... >>> RB Smissaert wrote: >>>> Unless I am overlooking something simple again, I don't think that will >>>> work. The thing is I need to replace at a specified position: >>>> >>>> Sub test() >>>> Dim str1 As String >>>> Dim str2 As String >>>> >>>> str1 = "123456789" >>>> >>>> str2 = "XX" >>>> >>>> str1 = Replace(str1, "4", str2, 4, 1) >>>> >>>> 'I need result to be "123XX56789" >>>> 'but will get "XX56789 >>>> MsgBox str1 >>>> End Sub >>> >>> Well, I'm not sure what exactly you're overlooking, but when I do this >>> in the Immediate Window: >>> >>> ?Replace("123456789", "4", "XX", 4, 1) >>> 123XX56789 >>> >>> It seems to do what you say you want it to? Maybe it's that I'm taking >>> what you say you need too literally, and what you really need is what >>> you allude you need? <g> IOW, you don't care what's at position four, >>> and it may be entirely non-unique, but you need to replace it? Yeah, in >>> that case, it's a mess. You can split and concatenate. Or you can >>> slide stuff around with CopyMemory. Is this something you need to do >>> repeatedly? >>> -- >>> .NET: It's About Trust! >>> http://vfred.mvps.org >>> >> > "RB Smissaert" <bartsmissa***@blueyonder.co.uk> wrote Here is one that will get the job done. You'd have to run timed tests to> Unless I am overlooking something simple again, I don't think that will > work. > The thing is I need to replace at a specified position: see if its faster than concatenation.... (it should be...) LFS Private Sub Form_Load() Debug.Print Replace("1234567", "4", "ABC") Debug.Print Replace("1234321", "2", "ABC", 2) Debug.Print Replace("1234321", "3", "XYZ", -1) Debug.Print Replace("1234567", "345", "X") Debug.Print Replace("1234321", "3", "XYZ", 1, 5) End Sub Function Replace(ByVal Source$, _ ByRef Find$, _ ByRef Insert$, _ Optional Repeat As Long = 1, _ Optional Position As Long = 0) As String Dim tmp As String Dim diff As Long Dim begin As Long Dim pos As Long Dim all As Boolean begin = 1 diff = Len(Insert) - Len(Find) If Repeat < 0 Then all = True Repeat = 2 End If If Position > 0 Then pos = InStr(Position, Source, Find) If pos = Position Then Repeat = 1 begin = pos Else Repeat = 0 End If End If Do While Repeat > 0 pos = InStr(begin, Source, Find) If pos > 0 Then tmp = Space$(Len(Source) + diff) Mid(tmp, 1) = Mid$(Source, 1, pos - 1) Mid(tmp, pos + Len(Insert)) = Mid$(Source, pos + Len(Find)) Mid(tmp, pos) = Insert begin = pos + Len(Insert) Repeat = Repeat - 1 If all Then Repeat = 2 Source = tmp Else Repeat = 0 End If Loop Replace = Source End Function Thanks, will have a look at that.
Will it work if the replacing string is longer than the string to be replaced? RBS Show quoteHide quote "Larry Serflaten" <serfla***@usinternet.com> wrote in message news:%23MNOaOnrJHA.2368@TK2MSFTNGP06.phx.gbl... > > "RB Smissaert" <bartsmissa***@blueyonder.co.uk> wrote >> Unless I am overlooking something simple again, I don't think that will >> work. >> The thing is I need to replace at a specified position: > > Here is one that will get the job done. You'd have to run timed tests to > see if its faster than concatenation.... (it should be...) > > LFS > > Private Sub Form_Load() > Debug.Print Replace("1234567", "4", "ABC") > Debug.Print Replace("1234321", "2", "ABC", 2) > Debug.Print Replace("1234321", "3", "XYZ", -1) > Debug.Print Replace("1234567", "345", "X") > Debug.Print Replace("1234321", "3", "XYZ", 1, 5) > End Sub > > > Function Replace(ByVal Source$, _ > ByRef Find$, _ > ByRef Insert$, _ > Optional Repeat As Long = 1, _ > Optional Position As Long = 0) As String > Dim tmp As String > Dim diff As Long > Dim begin As Long > Dim pos As Long > Dim all As Boolean > > begin = 1 > diff = Len(Insert) - Len(Find) > > If Repeat < 0 Then > all = True > Repeat = 2 > End If > > If Position > 0 Then > pos = InStr(Position, Source, Find) > If pos = Position Then > Repeat = 1 > begin = pos > Else > Repeat = 0 > End If > End If > > Do While Repeat > 0 > > pos = InStr(begin, Source, Find) > > If pos > 0 Then > tmp = Space$(Len(Source) + diff) > Mid(tmp, 1) = Mid$(Source, 1, pos - 1) > Mid(tmp, pos + Len(Insert)) = Mid$(Source, pos + Len(Find)) > Mid(tmp, pos) = Insert > begin = pos + Len(Insert) > Repeat = Repeat - 1 > If all Then Repeat = 2 > Source = tmp > Else > Repeat = 0 > End If > Loop > Replace = Source > > End Function > > > > "RB Smissaert" <bartsmissa***@blueyonder.co.uk> wrote Yes, look at the debug examples to see it in action.> Thanks, will have a look at that. > Will it work if the replacing string is longer than the string to be > replaced? One test that I did not include in the post is the case where the inserted text contains the search text, for example: Debug.Print Replace("123345", "3", "A3B", -1) There, the insert text (A3B) contains the find text (3), but the function will not loop forever, once the find text has been replaced, it moves on.... The result will be "12A3BA3B45" LFS "Larry Serflaten" <serfla***@usinternet.com> wrote It will have to be amended to catch the case where the find text is at> Here is one that will get the job done. You'd have to run timed tests to > see if its faster than concatenation.... (it should be...) the very end of the source string. The earlier version errors, this version catches that condition before it occurs: LFS Function Replace(ByVal Source$, _ ByRef Find$, _ ByRef Insert$, _ Optional Repeat As Long = 1, _ Optional Position As Long = 0) As String Dim tmp As String Dim diff As Long Dim begin As Long Dim pos As Long Dim all As Boolean begin = 1 diff = Len(Insert) - Len(Find) If Repeat < 0 Then all = True Repeat = 2 End If If Position > 0 Then pos = InStr(Position, Source, Find) If pos = Position Then Repeat = 1 begin = pos Else Repeat = 0 End If End If Do While Repeat > 0 pos = InStr(begin, Source, Find) If pos > 0 Then tmp = Space$(Len(Source) + diff) Mid(tmp, 1) = Mid$(Source, 1, pos - 1) If pos < Len(Source) Then Mid(tmp, pos + Len(Insert)) = Mid$(Source, pos + Len(Find)) End If Mid(tmp, pos) = Insert begin = pos + Len(Insert) Repeat = Repeat - 1 If all Then Repeat = 2 Source = tmp Else Repeat = 0 End If Loop Replace = Source End Function |
|||||||||||||||||||||||