Author Topic: FUNCTION Replace$() - roll your own and share it  (Read 7071 times)

0 Members and 1 Guest are viewing this topic.

FellippeHeitor

  • Guest
FUNCTION Replace$() - roll your own and share it
« on: July 26, 2020, 10:52:39 am »
Here's my contribution. I wrote this one and it is part of InForm - accessible from every program that uses the library and now also available here.

As discussed here, this is the type of function that usually makes more sense to each one who's written their own version for their own use, but you never know when your approach can be useful to others.

This one takes:
- a TempText$ string variable, which will be searched.
- A subString$ which will be looked for inside TempText$
- a NewString$ which will be the replacement.
- the CaseSensitive parameters indicates whether the search should be - you guessed it - case sensitive. Zero is false, Any non-zero value is true. And finally
- a TotalReplacements parameter which is actually used to return the number of occurrences that were replaced. You can ignore this one by passing a zero or, if you want to use this information, pass it a LONG variable that will return the total of replacements.
- The result of the replacements is returned by the function itself.

All that said, this is how one would use it:

Code: QB64: [Select]
  1. text$ = "The cats and dogs where playing, even though dogs don't like cats."
  2. newText$ = Replace$(text$, "cats", "monkeys", 0, total&)
  3.  
  4. PRINT "We replaced 'cats' with 'monkeys' in text$. Here's the original string$:"
  5. COLOR 4: PRINT text$
  6. COLOR 15: PRINT "Here's the result:"
  7. COLOR 10: PRINT newText$: PRINT
  8. COLOR 7: PRINT "Total occurrences: "; total&

  [ You are not allowed to view this attachment ]  

Code: QB64: [Select]
  1. FUNCTION Replace$ (TempText$, SubString$, NewString$, CaseSensitive AS _BYTE, TotalReplacements AS LONG)
  2.     DIM FindSubString AS LONG, Text$
  3.  
  4.     IF LEN(TempText$) = 0 THEN EXIT SUB
  5.  
  6.     Text$ = TempText$
  7.     TotalReplacements = 0
  8.     DO
  9.         IF CaseSensitive THEN
  10.             FindSubString = INSTR(FindSubString + 1, Text$, SubString$)
  11.         ELSE
  12.             FindSubString = INSTR(FindSubString + 1, UCASE$(Text$), UCASE$(SubString$))
  13.         END IF
  14.         IF FindSubString = 0 THEN EXIT DO
  15.         Text$ = LEFT$(Text$, FindSubString - 1) + NewString$ + MID$(Text$, FindSubString + LEN(SubString$))
  16.         TotalReplacements = TotalReplacements + 1
  17.     LOOP
  18.  
  19.     Replace$ = Text$

There's no option in this for "Whole word", as it wasn't something I personally needed. Looking forward to you guys' implementations.
« Last Edit: July 26, 2020, 11:05:36 am by FellippeHeitor »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: FUNCTION Replace$() - roll your own and share it
« Reply #1 on: July 26, 2020, 11:30:05 am »
  • Best Answer
  • Here is my "plain" one, need to make sure the new l's don't get replaced again and again in infinite loop:

    Code: QB64: [Select]
    1. _TITLE "strReplace$ test" 'b+ 2019-12-28     check to see that new l's don't get more replacements
    2. PRINT strReplace$("hello all", "ll", "l")
    3. PRINT strReplace$("hello all", "l", "lllll")
    4. PRINT strReplace$("lots of luck", "l", "lllll")
    5.  
    6. FUNCTION strReplace$ (s$, replace$, new$) 'case sensitive
    7.     strReplace$ = s$
    8.     p = INSTR(s$, replace$)
    9.     WHILE p
    10.         strReplace$ = MID$(strReplace$, 1, p - 1) + new$ + MID$(strReplace$, p + LEN(replace$))
    11.         p = INSTR(p + LEN(new$), strReplace$, replace$)
    12.     WEND
    13.  

    FellippeHeitor

    • Guest
    Re: FUNCTION Replace$() - roll your own and share it
    « Reply #2 on: July 26, 2020, 11:34:34 am »
  • Best Answer
  • True! I hadn't thought of corner cases like that.

    Offline bplus

    • Global Moderator
    • Forum Resident
    • Posts: 8053
    • b = b + ...
      • View Profile
    Re: FUNCTION Replace$() - roll your own and share it
    « Reply #3 on: July 26, 2020, 11:37:34 am »
  • Best Answer
  • True! I hadn't thought of corner cases like that.

    Yes I see ;-))

    Code: QB64: [Select]
    1. _TITLE "strReplace$ test" 'b+ 2019-12-28     check to see that new l's don't get more replacements
    2. PRINT fellReplace$("hello all", "ll", "l", -1, totreplace)
    3. PRINT fellReplace$("hello all", "l", "lllll", -1, totreplace)
    4. PRINT fellReplace$("lots of luck", "l", "lllll", -1, totreplace)
    5.  
    6. FUNCTION strReplace$ (s$, replace$, new$) 'case sensitive
    7.     strReplace$ = s$
    8.     p = INSTR(s$, replace$)
    9.     WHILE p
    10.         strReplace$ = MID$(strReplace$, 1, p - 1) + new$ + MID$(strReplace$, p + LEN(replace$))
    11.         p = INSTR(p + LEN(new$), strReplace$, replace$)
    12.     WEND
    13.  
    14. FUNCTION fellReplace$ (TempText$, SubString$, NewString$, CaseSensitive AS _BYTE, TotalReplacements AS LONG)
    15.     DIM FindSubString AS LONG, Text$
    16.  
    17.     IF LEN(TempText$) = 0 THEN EXIT SUB
    18.  
    19.     Text$ = TempText$
    20.     TotalReplacements = 0
    21.     DO
    22.         IF CaseSensitive THEN
    23.             FindSubString = INSTR(FindSubString + 1, Text$, SubString$)
    24.         ELSE
    25.             FindSubString = INSTR(FindSubString + 1, UCASE$(Text$), UCASE$(SubString$))
    26.         END IF
    27.         IF FindSubString = 0 THEN EXIT DO
    28.         Text$ = LEFT$(Text$, FindSubString - 1) + NewString$ + MID$(Text$, FindSubString + LEN(SubString$))
    29.         PRINT Text$
    30.         TotalReplacements = TotalReplacements + 1
    31.     LOOP
    32.  
    33.     fellReplace$ = Text$
    34.  

    FellippeHeitor

    • Guest
    Re: FUNCTION Replace$() - roll your own and share it
    « Reply #4 on: July 26, 2020, 11:39:34 am »
  • Best Answer
  • It's FUNCTION merge time, it seems 😉.

    Offline bplus

    • Global Moderator
    • Forum Resident
    • Posts: 8053
    • b = b + ...
      • View Profile
    Re: FUNCTION Replace$() - roll your own and share it
    « Reply #5 on: July 26, 2020, 11:41:54 am »
  • Best Answer
  • @FellippeHeitor  Yours is simple fix just replace 1 with len(replacement$) in INSTR arguments.
    « Last Edit: July 26, 2020, 11:43:38 am by bplus »

    Offline bplus

    • Global Moderator
    • Forum Resident
    • Posts: 8053
    • b = b + ...
      • View Profile
    Re: FUNCTION Replace$() - roll your own and share it
    « Reply #6 on: July 26, 2020, 12:00:43 pm »
  • Best Answer
  • TempodiBasic made a point about checking the source string and the string to replace for length but replacing with nothing seems OK:
    Code: QB64: [Select]
    1. _TITLE "strReplace$ test" 'b+ 2019-12-28     check to see that new l's don't get more replacements
    2. ' mod 2020-07-26 check for string lengths on source$ and replace$ but replacement new$ can be ""
    3. PRINT strReplace$("hello all", "ll", "l")
    4. PRINT strReplace$("hello all", "l", "lllll")
    5. PRINT strReplace$("lots of luck", "l", "lllll")
    6. PRINT strReplace$("hello all", "ll", "")
    7.  
    8. FUNCTION strReplace$ (s$, replace$, new$) 'case sensitive
    9.     DIM p AS LONG
    10.     IF LEN(s$) = 0 OR LEN(replace$) = 0 THEN BEEP: EXIT FUNCTION
    11.     strReplace$ = s$
    12.     p = INSTR(s$, replace$)
    13.     WHILE p
    14.         strReplace$ = MID$(strReplace$, 1, p - 1) + new$ + MID$(strReplace$, p + LEN(replace$))
    15.         p = INSTR(p + LEN(new$), strReplace$, replace$)
    16.     WEND


    Offline SMcNeill

    • QB64 Developer
    • Forum Resident
    • Posts: 3972
      • View Profile
      • Steve’s QB64 Archive Forum
    Re: FUNCTION Replace$() - roll your own and share it
    « Reply #7 on: July 26, 2020, 02:54:16 pm »
  • Best Answer
  • Somewhere around here, I have an over-engineered super-Steve Replace function.  Life's just been hectic as all get out here recently, and between everything in general, and swapping files and such from the old PC to the new one, I don't have a clue where it's at currently.

    The way my extreme version works is more or less:

    SUB Replace (text$, delimiter$, prefix$, search$, postfix$, replace$)

    The idea is:
    1) You give it a text string.   "Cats eat cat-food, not cat food.  That would be acatastrophy!"
    2) You choose a given delimiter.  "*"
    3) You choose a search string, making use of the delimiter$.  "cat*"
    4) You can choose a prefix or postfix to modify your work.  postfix$ = "-"
    5) You give it a replacement string.  "dog"

    The routine then searches your text$ for search$ (minus any delimiters, which would be "cat", from above). 
    The first "Cats" gets changed to "Dogs".  That "*" in "cat*" in the search string says we ignore any character after the "cat".
    The "cat-food" doesn't change, as we have a postfix saying keep "cat" if a "-" occurs after it.
    The next "cat" changes.
    The final "acatastrophy" doesn't change.  There's no open delimiter before the "cat" in our search string, like the "*" is after it, so that leading "a" prevents it from being altered.

    The final result would be "Dogs eat cat-food, not dog food.  That would be acatastrophy!"



    Now, if I can just sort out where the heck that routine actually went to in all this mess, I'll share it sometime....
    https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

    Offline TempodiBasic

    • Forum Resident
    • Posts: 1792
      • View Profile
    Re: FUNCTION Replace$() - roll your own and share it
    « Reply #8 on: July 27, 2020, 10:54:20 am »
  • Best Answer
  • Hi
    about REPLACE$ a string with a null string

    what do you think of this result?
    Code: QB64: [Select]
    1. _TITLE "strReplace$ test" 'b+ 2019-12-28     check to see that new l's don't get more replacements
    2. ' mod 2020-07-26 check for string lengths on source$ and replace$ but replacement new$ can be ""
    3. COLOR 0,7
    4. PRINT strReplace$("hello all", "ll", "l")
    5. 'PRINT strReplace$("hello all", "l", "lllll")
    6. 'PRINT strReplace$("lots of luck", "l", "lllll")
    7. 'PRINT strReplace$("hello all", "ll", "")
    8. PRINT strReplace$("111111111", "1", "")
    9.  
    10. FUNCTION strReplace$ (s$, replace$, new$) 'case sensitive
    11.     DIM p AS LONG
    12.     IF LEN(s$) = 0 OR LEN(replace$) = 0 THEN BEEP: EXIT FUNCTION
    13.     strReplace$ = s$
    14.     p = INSTR(s$, replace$)
    15.     WHILE p
    16.         strReplace$ = MID$(strReplace$, 1, p - 1) + new$ + MID$(strReplace$, p + LEN(replace$))
    17.         p = INSTR(p + LEN(new$), strReplace$, replace$)
    18.     WEND
    19.  

    Is it better  a dedicated flag to transform a string into a null string? Moreover you can choose between a BASIC type of null string and a C type null string (that ends with CHR$(0)...) with a flag, in this plain function you must put CHR$(0) as string to put into sourceString.
    Moreover if you want cancel a character you can use space character...o null character

    What do you think about?
    Programming isn't difficult, only it's  consuming time and coffee

    Offline phred

    • Newbie
    • Posts: 11
      • View Profile
    Re: FUNCTION Replace$() - roll your own and share it
    « Reply #9 on: July 27, 2020, 11:51:52 am »
  • Best Answer
  • Something I worked on a couple of years ago modeling Microsoft's function, which may or may not already be up here.
    phred

    Code: QB64: [Select]
    1. a$ = "Visit Microsoft!" 'original string
    2. b$ = "microsoft" 'search string
    3. c$ = "W3Schools" 'replacement string
    4.  
    5. a$ = "001x002X003x004x005"
    6. b$ = "x"
    7. c$ = "z"
    8.  
    9. 'f$ = "-1"
    10. 'f$ = "0"
    11.  
    12. d$ = replacesub$(a$, b$, c$, d$, e$, f$, g$)
    13.  
    14. FUNCTION replacesub$ (expression$, find$, replacewith$, start$, count$, compare$, exact$)
    15.     IF start$ = "" THEN start = 1 ELSE start = VAL(start$)
    16.     IF count$ = "" THEN count = -1 ELSE count = VAL(count$)
    17.     IF compare$ = "" THEN compare = 0 ELSE compare = VAL(compare$) 'default is for case sensitve
    18.  
    19.     IF find$ = "" OR replacewith$ = "" OR count = 0 THEN replacesub$ = expression$: EXIT FUNCTION
    20.     IF expression$ = "" OR start > LEN(expression$) THEN replacesub$ = "": EXIT FUNCTION
    21.  
    22.     IF exact$ <> "" THEN 'pad values to isolate whole word
    23.         find$ = " " + find$ + " "
    24.         replacewith$ = " " + replacewith$ + " "
    25.     END IF
    26.  
    27.     working$ = expression$
    28.     start2 = start
    29.  
    30.     rl = LEN(find$)
    31.     tally = 0
    32.     IF compare <> 0 THEN
    33.         working$ = LCASE$(working$)
    34.         find$ = LCASE$(find$)
    35.     END IF
    36.     rf = INSTR(start, working$, find$)
    37.     DO WHILE rf
    38.         tmp1$ = MID$(working$, 1, rf - 1) 'copy mid$ from start to beginning of found replacement
    39.         tmp2$ = MID$(working$, rf + rl) 'then copy from end of replacement to end of the text string (rf + rl)
    40.         working$ = tmp1$ + replacewith$ + tmp2$
    41.         start = rf
    42.         rf = INSTR(start, working$, find$)
    43.         tally = tally + 1: IF tally = count THEN EXIT DO
    44.     LOOP
    45.     working$ = MID$(working$, start2)
    46.     replacesub$ = working$
    47.  
    48.  
    49. 'VB - Replace$(expression, find, replacewith[, start[, count[, compare]]])
    50. 'Arguments
    51.  
    52. 'expression
    53. '    Required. String expression containing substring to replace.
    54. 'find
    55. '    Required. Substring being searched for.
    56. 'replacewith
    57. '    Required. Replacement substring.
    58. 'start
    59. '    Optional. Position within expression where substring search is to begin. If omitted, 1 is assumed. Must be used in conjunction with count.
    60. 'count
    61. '    Optional. Number of substring substitutions to perform. If omitted, the default value is -1, which means make all possible substitutions. Must be used in conjunction with start.
    62. 'compare
    63. '    Optional. Numeric value indicating the kind of comparison to use when evaluating substrings. See Settings section for values. If omitted, the default value is 0, which means perform a binary comparison.
    64. '
    65. 'if find = "" or replacewith = "" or count = 0 then return expression
    66. 'if expression = "" or start > len(expression) return ""
    67.  

    FellippeHeitor

    • Guest
    Re: FUNCTION Replace$() - roll your own and share it
    « Reply #10 on: July 27, 2020, 12:20:08 pm »
  • Best Answer
  • @FellippeHeitor  Yours is simple fix just replace 1 with len(replacement$) in INSTR arguments.

    I wonder if using _INSTRREV would make up for having to use LEN()... I'll look into that.

    @phred thanks for sharing. Why strings for numeric input?

    FellippeHeitor

    • Guest
    Re: FUNCTION Replace$() - roll your own and share it
    « Reply #11 on: July 27, 2020, 12:54:52 pm »
  • Best Answer
  • @bplus Using _INSTRREV... both my cats/monkeys and your LLLLL tests:

    Code: QB64: [Select]
    1. text$ = "The cats and dogs where playing, even though dogs don't like cats."
    2. newText$ = Replace$(text$, "cats", "monkeys", 0, total&)
    3.  
    4. PRINT "We replaced 'cats' with 'monkeys' in text$. Here's the original string$:"
    5. COLOR 4: PRINT text$
    6. COLOR 15: PRINT "Here's the result:"
    7. COLOR 10: PRINT newText$: PRINT
    8. COLOR 7: PRINT "Total occurrences: "; total&
    9.  
    10. PRINT: PRINT "Press any key...": SLEEP
    11.  
    12. 'b+ 2019-12-28     check to see that new l's don't get more replacements
    13. PRINT Replace$("hello all", "ll", "l", 0, total&);
    14. PRINT total&
    15. PRINT Replace$("hello all", "l", "lllll", 0, total&);
    16. PRINT total&
    17. PRINT Replace$("lots of luck", "l", "lllll", 0, total&);
    18. PRINT total&
    19.  
    20. FUNCTION Replace$ (TempText$, SubString$, NewString$, CaseSensitive AS _BYTE, TotalReplacements AS LONG)
    21.     DIM FindSubString AS LONG, Text$
    22.  
    23.     IF LEN(TempText$) = 0 OR LEN(SubString$) = 0 OR LEN(NewString$) = 0 THEN EXIT SUB
    24.  
    25.     Text$ = TempText$
    26.     TotalReplacements = 0
    27.     FindSubString = LEN(Text$)
    28.     DO
    29.         IF CaseSensitive THEN
    30.             FindSubString = _INSTRREV(FindSubString - 1, Text$, SubString$)
    31.         ELSE
    32.             FindSubString = _INSTRREV(FindSubString - 1, UCASE$(Text$), UCASE$(SubString$))
    33.         END IF
    34.         IF FindSubString = 0 THEN EXIT DO
    35.         Text$ = LEFT$(Text$, FindSubString - 1) + NewString$ + MID$(Text$, FindSubString + LEN(SubString$))
    36.         TotalReplacements = TotalReplacements + 1
    37.         IF FindSubString = 1 THEN EXIT DO
    38.     LOOP
    39.  
    40.     Replace$ = Text$
    41.  

    Offline Richard

    • Seasoned Forum Regular
    • Posts: 364
      • View Profile
    Re: FUNCTION Replace$() - roll your own and share it
    « Reply #12 on: July 27, 2020, 01:01:21 pm »
  • Best Answer
  • @Steve

    "Enhanced Replace All Routine"   (Feb 02,2014   Topic 11648)

    Offline bplus

    • Global Moderator
    • Forum Resident
    • Posts: 8053
    • b = b + ...
      • View Profile
    Re: FUNCTION Replace$() - roll your own and share it
    « Reply #13 on: July 27, 2020, 01:14:28 pm »
  • Best Answer
  • Hi all

    I was looking at TempodiBasic's post and realized you could do primitive math with strReplace$ !

    So I set out to make a demo of that and it turns out I started a little library of str Functions. I also discovered my strReplace FUNCTION could actually cause unintended recursion so I fixed it again!

    So here is the demo, remember when we first learned math with Stones or some other ubiquitous object?

    Code: QB64: [Select]
    1. _TITLE "strReplace$ test" 'b+ 2019-12-28     check to see that new l's don't get more replacements
    2. ' mod 2020-07-26 check for string lengths on source$ and replace$ but replacement new$ can be ""
    3. ' mod 2020-07-27 don't set strReplace$ = s$ because could cause unintented recursion.
    4.  
    5. DIM s$, stones$(1 TO 9), i AS INTEGER
    6. s$ = "Stone "
    7. stones$(9) = strCopies$(s$, 9)
    8.  
    9. FOR i = 1 TO 8
    10.     stones$(i) = strCopies$(s$, i)
    11.     PRINT "9 Stones MOD "; _TRIM$(STR$(i)); " Stone(s) is "; strEnclose$(strReplace$(stones$(9), stones$(i), ""), "{}")
    12.  
    13.  
    14. FUNCTION strReplace$ (s$, replace$, new$) 'case sensitive
    15.     DIM p AS LONG, sCopy$
    16.     IF LEN(s$) = 0 OR LEN(replace$) = 0 THEN BEEP: EXIT FUNCTION
    17.     sCopy$ = s$
    18.     p = INSTR(sCopy$, replace$)
    19.     WHILE p
    20.         sCopy$ = MID$(sCopy$, 1, p - 1) + new$ + MID$(sCopy$, p + LEN(replace$))
    21.         p = INSTR(p + LEN(new$), sCopy$, replace$)
    22.     WEND
    23.     strReplace$ = sCopy$
    24.  
    25. FUNCTION strEnclose$ (s$, str1or2$) ' enclose with "" or {}, (), []...
    26.     IF LEN(str1or2$) = 2 THEN
    27.         strEnclose$ = LEFT$(str1or2$, 1) + s$ + RIGHT$(str1or2$, 1)
    28.     ELSEIF LEN(str1or2$) = 1 THEN
    29.         strEnclose$ = str1or2$ + s$ + str1or2$
    30.     ELSE
    31.         BEEP
    32.     END IF
    33.  
    34. FUNCTION strCopies$ (s$, numberOfCopies AS INTEGER)
    35.     DIM i AS INTEGER
    36.     FOR i = 1 TO numberOfCopies
    37.         strCopies$ = strCopies$ + s$
    38.     NEXT
    39.  
    40.  
    41.  

    @FellippeHeitor  well your version looks a little more complex than it has to be

     IF FindSubString = 0 THEN EXIT DO
     IF FindSubString = 1 THEN EXIT DO

    but as author of _INSTRREV, I can see why you might favor it. ;-))


    Oh hey, if we count replacements we have the two elements for complete division report.
    « Last Edit: July 27, 2020, 01:25:36 pm by bplus »

    Offline TempodiBasic

    • Forum Resident
    • Posts: 1792
      • View Profile
    Re: FUNCTION Replace$() - roll your own and share it
    « Reply #14 on: July 27, 2020, 07:53:59 pm »
  • Best Answer
  • @bplus
    great the stone math....
    hey but you have found a bug of OPTION _EXPLICIT
    ... it lets's to use Subprograms without an END at the end of the main!
    For my hobbist eyes it is horrible (I think because there is a voice of Pascal/TurboPascal into my mind)
    would Pascal let you execute the main without closing it with END. ?
    Would C let you execute the main without closing it with } ?
    Also QB45 does this thing, but it has Subroutines first of the main and after the declaration of themselves.
    Programming isn't difficult, only it's  consuming time and coffee