Announcement

Collapse
No announcement yet.

ReplaceAll Question...

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • ReplaceAll Question...

    I am using the code by written by Borje Hagsten to ReplaceAll. It works very well. The link to the code is found at:

    http://www.powerbasic.com/support/pb...ght=replaceall

    I have been trying to figure out what I need to do to modify the code to replace the first instance of the word only. I added an argument:

    occurrence = 0 replace all instances
    occurrence = 1 replace first instance

    The problem is I cannot figure out where to stop after the first occurrence, do the replace and exit.

    I hope that someone can take a look at the code and help me.

    Thanks,
    Spencer

  • #2
    In the original routine, you can count the number of characters replaced; when that equals the number of characters in the 'search for' you must be done and can exit.

    If you can't figure out how to count the number of characaters replaced in the original code, you can eschew the use of that code and keep it simple:

    Code:
    ' bCase [in]: TRUE, the search is case-sensitive; FALSE it ain't.
    
    ' returns: TRUE, a replacement was made
               FALSE, it wasn't
    
    FUNCTION ReplaceFirstOccurence (Main AS STRING, searchFor AS STRING, replaceWith as string, BYVAL bCase AS LONG) AS LONG
    
         LOCAL iPos AS LONG, bReplace AS LONG
         LOCAL WMain AS STRING, WSearchFor AS STRING
    
         IF ISFALSE bCase THEN
            wMain  =  LCASE$(Main)
            wSearchFor = LCASE$(SearchFor)
         ELSE
            wMain      = Main
            wSearchFor = SearchFor
         END IF
    
         iPos = INSTR (wMain, wsearchFor) 
         IF ISTRUE iPos THEN 
              MID$(Main, iPos, Len(searchfor)) = ReplaceWith
              bReplace = %TRUE
         END IF
    
         FUNCTION = bReplace
      
    END FUNCTION
    MCM
    Michael Mattias
    Tal Systems Inc. (retired)
    Racine WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3

      Hey Spencer,

      [Updated 2008/05/06]


      Code:
        
      #COMPILE EXE '#CC 4.04#
      #DIM ALL
      #INCLUDE "Win32Api.inc" '#2005-01-27#
      '______________________________________________________________________________
       
      FUNCTION ReplaceAll(BYVAL txt AS STRING, BYVAL s AS STRING, BYVAL r AS STRING, _
                                              BYVAL mc AS LONG, BYVAL wo AS LONG, fnd AS LONG, CountOnly AS LONG, _
                                              BYVAL Occurence AS LONG) AS STRING
        LOCAL x AS LONG, x2 AS LONG, Letter AS BYTE PTR, Letter2 AS BYTE PTR, Letter3 AS BYTE PTR
        LOCAL ckLet AS BYTE PTR, OldLetter AS BYTE PTR, oldx AS LONG, Flag AS LONG, sln AS LONG
        LOCAL tmpCountOnly AS LONG, dif AS LONG, rln AS LONG, oln AS LONG, fLet AS BYTE
        LOCAL txt2 AS STRING, txt3 AS STRING
       
        IF LEN(s) = 0 OR LEN(txt) < LEN(s) THEN EXIT FUNCTION 'No point in continuing..
        Fnd = 0
        IF Occurence = 0 THEN Occurence = - 1
       
        IF CountOnly = 0 THEN txt3 = txt                  'Replace wanted, so keep a copy of original text
        oln = LEN(txt)                                    'Get whole text's length
        sln = LEN(s)                                      'Get search text's length
        tmpCountOnly = 1                                  'Set tmp flag so we can get count first
       
        IF mc = 0 THEN                                    'If not MATCH CASE
            CharUpperBuff BYVAL STRPTR(txt), oln            'Compare both search and text as UCASE strings
            CharUpperBuff BYVAL STRPTR(s), sln
        END IF
       
        GOTO BeginSearch                                  'Ignore replace at this point
       
        BeginReplace:
        tmpCountOnly = 0                                  'Now we have count, so reset this one
        rln = LEN(r)                                      'Get replace text's length
        dif = fnd * (rln - sln)                           'Calculate occurrences and caclculate needed space
        txt2 = SPACE$(oln + dif)                          'Allocate space enough for replacements
        Letter2 = STRPTR(txt2)                            'Set pointer to resulting text
        Letter3 = STRPTR(txt3)                            'Set pointer to original text
       
        BeginSearch:
        Letter = STRPTR(txt)                              'Set pointer to text
        fLet = ASC(s)                                     'Get first letter in search text
       
        FOR x = 1 TO oln
            IF @Letter = fLet THEN                          'If first letter matches
                flag = 1                                      'Set found flag
       
                IF wo THEN                                    'If whole words only
                    IF x > 1 THEN                               'If position is > 1
                        ckLet = Letter - 1                        'Check letter before
                        flag = (IsCharAlphaNumeric(@ckLet) = 0)   'Flag becomes zero if not ok
                    END IF
                    IF Flag THEN                                'If still in business
                        IF x < oln - sln THEN                     'If position is < len(txt) - len(search)
                            ckLet = Letter + sln                    'Check letter after
                            flag = (IsCharAlphaNumeric(@ckLet) = 0) 'Flag becomes zero if not ok
                        END IF
                    END IF
                END IF
       
                IF Flag THEN                                  'If still in business
                    OldLetter = Letter : oldx = x               'Store positions
                    FOR x2 = 2 TO sln                           'Check against rest of search string
                        INCR Letter : INCR x                      'Increase pointers
                        IF @Letter <> ASC(s, x2) THEN             'If anything different
                            Letter = OldLetter : x = oldx           'Reset positions
                            flag = 0 : EXIT FOR                     'Reset flag and move on
                        END IF
                    NEXT
                    IF Flag THEN                                'If valid sub string found
                        IF TmpCountOnly = 0 THEN                  'If not in counting mode but in replacing mode
                            IF Occurence = 0 THEN                   'All occurence have been treated
                                Letter = OldLetter                    'Put back old pointer
                                x = oldx                              'Put back old pointer
                                Flag = 0                              'Lie and say nothing found
                            ELSE
                                DECR Occurence                        'Keep count (Text will be replaced)
                            END IF
                        END IF
                    END IF
                END IF
            END IF
       
            IF tmpCountOnly THEN                            'If initial count
                IF flag THEN                                  'Match, so increase counter and reset flag
                    INCR fnd : flag = 0
                END IF
            ELSE                                            'Else, if replace action
                IF flag = 0 THEN                              'No match
                    ckLet = Letter3 + x - 1                     'Set pointer in original string
                    @Letter2 = @ckLet                           'Just copy bytes from original string
                ELSE                                          'Else perfect match, so
                    POKE$ Letter2, r                            'Poke replace string into place
                    Letter2 = Letter2 + rln - 1                 'Increase pointers in resulting string
                    flag = 0                                    'And reset flag
                END IF
                INCR Letter2
            END IF
       
            INCR Letter                                     'Next letter
        NEXT
       
        IF CountOnly THEN                                 'If CountOnly flag was set
            txt2 = FORMAT$(fnd)                             'Return count both as string and in fnd variable
        ELSE
            IF fnd AND tmpCountOnly THEN
                GOTO BeginReplace                             'Else jump to label and replace found occurrences
                                                                                                            '(Function will then return resulting text in txt2)
                                                                                                            '(fnd will still return count..)
                                                                                                            'return result - or count, on Count all..
            END IF
        END IF
       
        FUNCTION = txt2
       
      END FUNCTION
      '______________________________________________________________________________
       
      FUNCTION PBMAIN() AS LONG
        'LOCAL FoundCount AS LONG
        '
        'MessageBox %HWND_DESKTOP, BYCOPY _
        '  ReplaceAll("--x----x----x--", "x", "o", 0, 0, FoundCount, 0, 2) & _
        '  $CRLF & "FoundCount =" & STR$(FoundCount), _
        '  BYCOPY "Börge's ReplaceAll", %MB_ICONINFORMATION OR %MB_OK
       
        LOCAL FoundCount AS LONG
        LOCAL sourceText AS STRING
        LOCAL i AS INTEGER
       
        sourceText = "path=\\172.16.12.55\data\workarea\archive\1.pdf"
        STDOUT sourceText
        sourceText = ReplaceAll(sourceText, "path=\\172.16.12.55\data\workarea\","<?xml version=1.0?><document><Identifier>\\courage\xyz\pdf\xml\",0,0, FoundCount, 0, 0)
        STDOUT sourceText
        sourceText = ReplaceAll(sourceText, ".pdf",".pdf</Identifier>",0,0, FoundCount, 0, 0)
        STDOUT sourceText
        sourceText = ReplaceAll(sourceText, "<documentField name=""FdnName""/>","<FdnName>test</FdnName>",0,0, FoundCount, 0, 0)
        STDOUT sourceText
       
        PRINT : PRINT "Press any key or click to continue..." : MOUSE ON : MOUSE 3, UP : WAITKEY$
       
      END FUNCTION
      '______________________________________________________________________________
      '
       
      Last edited by Pierre Bellisle; 6 May 2008, 02:46 PM.

      Comment


      • #4
        I added the code as Pierre suggested. I now receive the following error as seen below. Does this make any sense to you?

        PowerBASIC Console Compiler
        PB/CC Version 4.04
        Copyright (c) 1998-2007 PowerBasic Inc.
        Venice, Florida USA
        All Rights Reserved

        Primary source: C:\CUSTOM~1\Tools\eLibrary.bas {50263 total lines}
        Target compilation: eLibrary.exe
        Compile time: 0.2 seconds, at 15078900 lines/minute

        12164 bytes compiled code, 26783 bytes RTLibrary,
        1116 bytes string literals, and 3064 bytes dgroup.
        Executable stack size: 1048576 bytes.
        Disk image: 45056 bytes Memory image: 30963 bytes.

        Component Files:
        ----------------
        C:\CUSTOM~1\TOOLS\ELIBRARY.BAS
        C:\PBCC40\WINAPI\WIN32API.INC
        REPLACEALL.INC
        ==============================
        Compile succeeded at 12:07:43 AM on 5/5/2008

        Begin Debug at 12:07:43 AM on 5/5/2008
        HEAP[eLibrary.exe]:
        Heap block at 0014C170 modified at 0014C358 past requested size of 1e0<0A>
        HEAP[eLibrary.exe]:
        Invalid Address specified to RtlSizeHeap( 00140000, 0014C178 )<0A>
        HEAP[eLibrary.exe]:
        Heap block at 0014C170 modified at 0014C358 past requested size of 1e0<0A>
        HEAP[eLibrary.exe]:
        Invalid Address specified to RtlFreeHeap( 00140000, 0014C178 )<0A>
        End Debug at 12:07:56 AM on 5/5/2008

        Comment


        • #5
          Well, since you don't call those Rtl functions yourself, my best guess is that somewhere in your code you are misuing a pointer (writing) or failing to set a correct buffer size.

          This is the line which suggests that:
          >Heap block at 0014C170 modified at 0014C358 past requested size of 1e0<0A>

          But to get practical here...

          Mr. Hagsten's code is a general-purpose function designed to do a case-insensitive replace of all occurrences of a target string... a case-insensitive PowerBASIC 'REPLACE' if you will.

          Your application only wants to replace the first occurrence of a target string.

          Call me naive or insensitive, but for that purpose why not keep it simple and just use the tools provided by the compiler (eg INSTR and MID$=)?

          MCM
          Michael Mattias
          Tal Systems Inc. (retired)
          Racine WI USA
          [email protected]
          http://www.talsystems.com

          Comment


          • #6
            Hey Spencer,

            I did only few tests, I reposted the whole thing above
            to make sure that we are working on the same code.
            If it still explode, what strings and options are you using
            when calling the function.

            Comment


            • #7
              Hi Michael,

              I am taking advantage of all of the search and replace functionality in the ReplaceAll code. Such as case sensitive and insensitive, wholeword and most often replacing all occurrences. I have a dataset that I need to do many modifications to. Last run it made about 70,000 changes. The search and replace settings are created and saved in a configuration file. My application reads this configuration file, crawls the drive to find candidate files based on a user specified starting location and makes all of the changes as needed. This dataset is challenging as there are certain occurrences of the same word that need to be changed to different words. The only way I can figure out how to do this easily is change the first occurrence of the word, reset the search replace term and find the next occurrence of the search term, which is now the first occurrence again.

              I had thought about using the tools provided by the compiler, but the ReplaceAll code is fast, and I want to provide the same functionality for search configured in the Search and Replace configuration I provide.

              So you are not being insensitive, just asking good questions.

              Spencer

              Comment


              • #8
                as there are certain occurrences of the same word that need to be changed to different words.
                I sure hope this is a 'one-off' conversion-type application (for which you are receiving many many dollars), because this design stinks.

                That is, there simply must be a better way to get the desired output.

                However..
                I want to provide the same functionality for search configured in the Search and Replace configuration I provide.
                .. it doesn't sound like it.

                Oh, wait a minute... this is one of those "I am going to cooperate with a non-cooperating application" things, isn't it?

                MCM
                Michael Mattias
                Tal Systems Inc. (retired)
                Racine WI USA
                [email protected]
                http://www.talsystems.com

                Comment


                • #9
                  Then again... you could keep it simple even given you need to replace the 'nth' occurrence with a different string...

                  ReplaceWith() [IN]: Array of replacement tokens with the value at subscript 'n' being the replacement
                  for occurrence n; ReplaceWith(1) replaces first occurrence, ReplaceWith(2) the second, etc.

                  Code:
                  FUNCTION ReplaceAllOccurrences (Main AS STRING, searchFor AS STRING, replaceWith() as string, BYVAL bCase AS LONG) AS LONG
                  
                       LOCAL iPos AS LONG, bReplace AS LONG
                       LOCAL WMain AS STRING, WSearchFor AS STRING
                       LOCAL ipos() AS LONG, ihit AS LONG, nSub AS LONG, iSub as LONG, offset as LONG
                  
                       'create working string of correct case for search
                       IF ISFALSE bCase THEN
                          wMain  =  LCASE$(Main)
                          wSearchFor = LCASE$(SearchFor)
                       ELSE
                          wMain      = Main
                          wSearchFor = SearchFor
                       END IF
                  
                     ' Build a table of positions in original string of all occurrences of 'Searchfor'
                     ' up to the maximum number of replacements specified:
                  
                       REDIM iPOs (UBOUND (ReplaceWith,1)) ' hold all specified replacements 
                  
                       iStart=   1    ' where else?
                       iSub  =   0    ' we increment AFTER we find a hit..
                  
                       DO 
                          iHit =  INSTR (iStart, wMain, SearchFor) 
                  
                          IF iHit THEN
                             INCR iSub
                             iPos(iSub) = iHit
                             iStart     = iHit + LEN(SearchFor)   ' assume no embedded finds.  Use +1 to handle embedded. 
                           ELSE           ' no more hits found
                              EXIT DO
                           END IF
                       LOOP WHILE iStart <= LEN(wMain) AND iSub <= UBOUND (iPos,1) 
                  
                       ' at this point isub holds the number of occurrences actually found up to 
                       ' the maximum number of replacement strings. Since we want to reuse that variable
                       ' let's put it somewhere else.
                  
                       nSUB = iSub
                  
                       iSub   =  1
                       offset =  0             ' no replacements made yet so no length adjustment req'd.
                       DO WHILE iSub <= nSub   ' never executes if no hits found
                             MID$(main, ipos(iSub) + 0ffset, LEN(SearchFor)) = Replacewith(iSub) 
                             ' adjust offset for characters added to and subtracted from 
                             ' main string by the last replace. 
                             offset  =  offset + LEN(Replacewith(iSub)) - LEN(searchFor) 
                             INCR iSub
                       LOOP   
                                
                       FUNCTION = ISTRUE (nSub)
                  
                    
                  END FUNCTION
                  Not tested but I'll bet it's close. You can speed that up a bit by getting LEN(searchfor) and LEN(wMain) once instead of multiple times as shown.

                  You could do pretty much the same thing to the original procedure by passing an array of "replacewith" strings.

                  MCM
                  Michael Mattias
                  Tal Systems Inc. (retired)
                  Racine WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment


                  • #10
                    Hi Pierre,

                    In the example above if you change pbmain to the following and then debug the program you will see the heap error.

                    Spencer

                    Code:
                    FUNCTION PBMAIN() AS LONG
                    LOCAL FoundCount AS LONG
                    LOCAL sourceText AS STRING
                    
                    LOCAL i AS INTEGER
                        STDOUT sourceText
                        sourceText = "path=\\172.16.12.55\data\workarea\archive\1.pdf"
                    
                        sourceText = ReplaceAll(sourceText, "path=\\172.16.12.55\data\workarea\","<?xml version=1.0?><document><Identifier>\\courage\xyz\pdf\xml\",0,0, FoundCount, 0, 0)
                        sourceText = ReplaceAll(sourceText, ".pdf",".pdf</Identifier>",0,0, FoundCount, 0, 0)
                        sourceText = ReplaceAll(sourceText, "<documentField name=""FdnName""/>","<FdnName>test</FdnName>",0,0, FoundCount, 0, 0)
                    
                    END FUNCTION

                    Comment


                    • #11
                      If the stepping debugger is failing with those messages, the problem is in the stepping debugger. You need to send a report to [email protected] along with source code, compiler version and any required data files. They may want hardware and O/S info, too, but heap error handling in the stepping debugger is above my pay grade so I'd probably just include that in my report.

                      This forum is not an official communications venue for bug reports.
                      Michael Mattias
                      Tal Systems Inc. (retired)
                      Racine WI USA
                      [email protected]
                      http://www.talsystems.com

                      Comment


                      • #12
                        Hi Michael,

                        I will submit the report as you suggest. Thanks for your help. I will try your code suggestion as well.

                        Thanks,
                        Spencer

                        Comment


                        • #13
                          Spencer,
                          No need to submit anything,
                          I found it, problem was in original code.

                          Add
                          Fnd = 0
                          to the beginning of the function,
                          this will prevent errors with invalid
                          Fnd preset on subsequents call.
                          Last edited by Pierre Bellisle; 6 May 2008, 12:18 PM.

                          Comment


                          • #14
                            Hi Spencer,

                            I looked at your code and you are overwriting critical sections of Windows system memory through your pointer variables. The PowerBASIC debugger is functioning correctly, until it needs to use memory which has been damaged.

                            I found that upon returning from the
                            Code:
                            sourceText = ReplaceAll(sourceText, "<documentField name=""FdnName""/>","<FdnName>test</FdnName>",0,0, FoundCount, 0, 0)
                            function call the heap error occurs. You can also see this same heap error when trying to modify the variable txt2 before exiting the function. This indicates that the memory for this variable and possibly others has been corrupted. I am afraid this will take a bit of detective work on your part to locate the actual culprit code. Best of luck.
                            Sincerely,

                            Steve Rossell
                            PowerBASIC Staff

                            Comment


                            • #15
                              The PowerBASIC debugger is functioning correctly, until it needs to use memory which
                              has been damaged...
                              .... at which time it displays some cryptic messages designed to scare the snot out of the user?
                              Michael Mattias
                              Tal Systems Inc. (retired)
                              Racine WI USA
                              [email protected]
                              http://www.talsystems.com

                              Comment


                              • #16
                                Originally posted by Michael Mattias View Post
                                .... at which time it displays some cryptic messages designed to scare the snot out of the user?
                                No, please read the messages. They are Window's messages, not from PowerBASIC. Please call your uncle Bill.

                                Comment


                                • #17
                                  Code:
                                  Begin Debug at 12:07:43 AM on 5/5/2008
                                  HEAP[eLibrary.exe]: 
                                  Heap block at 0014C170 modified at 0014C358 past requested size of 1e0<0A>
                                  HEAP[eLibrary.exe]: 
                                  Invalid Address specified to RtlSizeHeap( 00140000, 0014C178 )<0A>
                                  HEAP[eLibrary.exe]: 
                                  Heap block at 0014C170 modified at 0014C358 past requested size of 1e0<0A>
                                  HEAP[eLibrary.exe]: 
                                  Invalid Address specified to RtlFreeHeap( 00140000, 0014C178 )<0A>
                                  End Debug at 12:07:56 AM on 5/5/2008
                                  It sure looks like these ARE "PowerBASIC" messages...that is, they are the output of a PowerBASIC Inc. Product.

                                  When you are a Real User and a product displays a message, I just cannot find it unreasonable to assume the message text was provided by the publisher.

                                  I know that is not the kind of text you normally use for error messages; but 'newbies' to the PB product don't have 15+ years looking at "genuine" PB error messages; and would have no way of knowing that is not what a "normal" message looks like.

                                  I don't know that it's even possible, but perhaps you could cushion the blow by inserting before the "End Debug at..."

                                  "An error occrred during the debugger session. Please don't blame us for the message text, as it was provided by Uncle Bill"

                                  Something like that, anyway.
                                  Michael Mattias
                                  Tal Systems Inc. (retired)
                                  Racine WI USA
                                  [email protected]
                                  http://www.talsystems.com

                                  Comment


                                  • #18
                                    I'm really surprised you are confused, Michael.

                                    The product is a debugger. It displays any text sent to the debug stream. Even if sent by the target program. Even if sent by Windows. Even if sent by you. That's what debuggers do. Text in the debug stream has no signature. It has no thumb print. It has no DNA sample. It is text. It is displayed because it's there. The product is a debugger and it displays whatever is present in the debug stream.

                                    Next you'll want us to censor the PRINT statements you put in your own program? {smile}

                                    Have a nice day.

                                    Bob Zale
                                    PowerBASIC Inc.

                                    Comment


                                    • #19
                                      You should not be surprised I'm confused.....since I don't use the stepping debugger. (Real Men and all that).

                                      But it's pretty clear to me OP (Mr. Shearer) was at best befuddled and at most alarmed by the messages he got. I was suggesting that some changes to exactly what appears on the user's screen might make it easier to use and understand.

                                      As I said before, I don't know that it's even possible - and based on your comments here about "what debuggers do" I'm thinking it may well not be possible at all - but anything that makes your product easier to use and understand certainly is worth some consideration.

                                      MCM
                                      Michael Mattias
                                      Tal Systems Inc. (retired)
                                      Racine WI USA
                                      [email protected]
                                      http://www.talsystems.com

                                      Comment


                                      • #20
                                        Originally posted by Michael Mattias View Post
                                        ...but anything that makes your product easier to use and understand certainly is worth some consideration.
                                        Of course. That's exactly why one of our engineers spent over an hour going over his code.

                                        And, since you're such a bright guy, I'm sure you now realize how unpleasant it is to read about "scaring the snot" out of anyone.

                                        Best regards,

                                        Bob Zale
                                        PowerBASIC Inc.

                                        Comment

                                        Working...
                                        X