These BIFs help you find and replace text in strings in an easy and, most importantly, readable and maintainable manner. Say goodbye to complex and undecipherable code ridden with MOVELs and CATs op codes.
The previous TechTip showed an example of how to insert a piece of text in the middle of an existing string, using both old, traditional and new, BIF-esque approaches. However, it relied on the fact that the program "knew" beforehand where it needed to insert the text, which denotes a certain lack of flexibility. Another problem was the complexity of the code, albeit it was much simpler than the traditional approach, which requires splitting and stitching operations to insert text in the middle of the string.
Making the "New" More Dynamic with %SCAN
To make the program more flexible, you'd need to dynamically determine the position where you need to insert the new text and a simpler way to insert it. Let's approach this problem with three new BIFs, starting with the
%SCAN BIF. %SCAN has two mandatory parameters and an optional one. As you'd expect, what you are looking for (search argument) and where to look (source string) are the mandatory parameters, in that order. The optional parameter tells the BIF in which position of the source string it should start looking. If you don't specify it, it defaults to 1--
i.e., the beginning of the source string. This BIF returns the position in which the search argument begins or zero if the search argument was not found in the source string. Here's an example of different %SCAN usage scenarios:
D source S 15A INZ ('Dr. Doolittle')
D pos S 5U 0
C EVAL pos = %SCAN('oo' : source)
* After the EVAL, pos = 6 because 'oo' begins at position 6 in
* 'Dr. Doolittle'.
C EVAL pos = %SCAN('D' : source : 2)
* After the EVAL, pos = 5 because the first 'D' found starting from
* position 2 is in position 5.
C EVAL pos = %SCAN('abc' : source)
* After the EVAL, pos = 0 because 'abc' is not found in
* 'Dr. Doolittle'.
C EVAL pos = %SCAN('Dr.' : source : 2)
* After the EVAL, pos = 0 because 'Dr.' is not found in
* 'Dr. Doolittle', if the search starts at position 2.
Improving It with %REPLACE
You're probably thinking, "This is great! Finally, I have a way find that character in the middle that I have to replace." Then you can proceed to cut the two pieces of the string and stich them together with the plus sign (+) operator a bit more dynamically. Well, that's OK, but there's a somewhat simpler way: just use %REPLACE.
All you need to do to use this BIF is specify the replacement string, the source string, and the position returned by %SCAN. If you don't specify this third parameter, the
%REPLACE BIF
will start replacing at the beginning of the string. Finally, you can also indicate how many characters you want to replace by specifying that number in a fourth parameter. By the way, the third and fourth parameters are optional. I know that this may sound a bit confusing, so let's go over a few examples together:
D var1 S 30A INZ('London') VARYING
D var2 S 30A INZ('Lisbon') VARYING
D var3 S 30A INZ('Berlin') VARYING
D fixed1 S 15A INZ('Portugal')
D date S D INZ(D'2014-10-28')
D result S 100A VARYING
C EVAL result = var1 + ', ' + 'PT'
* result = 'London, PT'
* 1. %REPLACE with 2 parameters to replace text at beginning of string:
C EVAL result = %REPLACE('Prague': result)
* result = 'Prague, PT'
* 2. %REPLACE with 3 parameters to replace text at specified position:
C EVAL result = %REPLACE(var3: result:
%SCAN(',': result) + 2)
* result = 'Prague, Berlin'
These first two examples are quite straightforward, but they fail to show the true potential of this BIF. Let's see how to solve the problem from the previous TechTip using all four parameters that %REPLACE offers:
* 3. %REPLACE with 4 parameters to insert text:
C EVAL result = %REPLACE(', ' + var2: result:
%SCAN(',': result): 0)
* result = 'Prague, Lisbon, Berlin'
I'm using %SCAN to determine where the first comma is, and then I'm replacing that comma with ' Lisbon,'. I could use a statement similar to this to perform the task from the previous TechTip that required a lot more coding in both the traditional and BIF-esque scenarios. But you can do even more with this BIF:
* 4. %REPLACE with 4 parameters to replace strings with different length
C EVAL result = %REPLACE('Washington': result:
1 : %scan (',': result) - 1)
* result = '
Washington, Lisbon, Berlin'
* 5. %REPLACE with 4 parameters to delete text:
C EVAL result = %REPLACE('': result:
1: %scan (',': result) + 1)
* result = 'Lisbon, Berlin'
* 6. %REPLACE with 4 parameters to add text to the end of the string:
C EVAL result = %REPLACE(', ' + %CHAR(date):
result:
%LEN(result) + 1: 0)
* result = 'Lisbon, Berlin,
2014-10-28'
* 7. %REPLACE with 3 parameters to replace fixed-length text at
* specified position: (fixed1 has fixed-length of 15 chars)
C EVAL result = %REPLACE(fixed1:
result:
%SCAN(',': result) + 2)
* result = 'Lisbon, Portugal -28'
* 8. %REPLACE with 4 parameters to prefix text at beginning:
C EVAL result = %REPLACE('Somewhere else: ':
result: 1: 0);
* result = 'Somewhere else: 'Lisbon, Portugal -28'
This is a very powerful BIF, but it can be a little difficult to wrap your head around, especially because, like me, you're probably used to the "find/replace" functionality of the modern text editors. Well, there's also a BIF that emulates that functionality.
The Bad and Good News of %SCANRPL
%SCANRPL (Scan and Replace characters) is more intuitive than %REPLACE: there's the text to find (the "scan string" parameter), then the text to replace it (that's the "replacement" parameter), and finally the source string (or "source" in the documentation). Optionally, you can specify where to start looking (the "scan start" parameter) and how many characters to look for (the "scan length" parameter). %SCANRPL also has a very cool feature: it replaces all occurrences of the scan string with the replacement string in one single step. Here are a few examples adapted from IBM's ILE RPG Reference Manual:
C EVAL string1 = 'See NAME. See NAME run.'
+ ' Run NAME run.'
* 1. All occurrences of "NAME" are replaced by the
* replacement value. In the first case,
* the resulting string is shorter than the source
* string, since the replacement string is shorter
* than the scan string. In the second case, the
* resulting string is longer.
C EVAL string2 = %SCANRPL('NAME' :'Tom' :
C string1)
* string2 = 'See Tom. See Tom run. Run Tom run.'
C EVAL string2 = %SCANRPL('NAME' : 'Jenny' :
C string1)
* string2 = 'See Jenny. See Jenny run. Run Jenny run.'
* 2. All occurrences of ** are removed from the string.
* The replacement string, '', has zero length.
C EVAL string3 = '*Hello**There**Everyone*'
C EVAL string2 = %SCANRPL('**' : '' : string3)
* string2 = '*HelloThereEveryone*'
* 3. All occurrences of "NAME" are replaced by "Tom"
* starting at position 6. Since the first "N" of
* the first "NAME" in the string is not part of the
* source string that is scanned, the first "NAME"
* is not considered replaceable.
C EVAL string2 = %SCANRPL('NAME' : 'Tom' :
C string1 : 6)
* string2 = 'See NAME. See Tom run. Run Tom run.'
* 4. All occurrences of "NAME" are replaced by "Tom"
* up to length 31. Since the final "E" of
* the last "NAME" in the string is not part of the
* source string that is scanned, , the final "NAME"
* is not considered replaceable.
C EVAL string2 = %SCANRPL('NAME' : 'Tom' :
C string1 : 1 : 31)
* string2 = 'See Tom. See Tom run. Run NAME run.'
* 5. All occurrences of "NAME" are replaced by "Tom"
* from position 10 for length 10. Only the second
* "NAME" value falls in that range.
C EVAL string2 = %SCANRPL('NAME' : 'Tom' :
C string1 : 10 : 10)
* string2 = 'See NAME. See Tom run. Run NAME run.'
Just a word of caution: %SCANRPL was introduced with V7.1, so if your IBM i is in an older release, you won't be able to use it (as far as I know). But even if you're in V7.1, SEU won't recognize it, because it stopped keeping up with RPG and COBOL enhancements in V6R1. That's the bad news. The good news is that by using %SCANRPL, the code to perform the insertion task from the previous TechTip becomes this:
C EVAL result = %SCANRPL(', 1' : ', 0, 1' : base_str)
It's that simple! How cool is that?
These last two TechTips discuss very important BIFs that can really help you. I'll show some practical (and fun) examples using them in the next TechTip. Until then, feel free to speak your mind in the Comments section below or in the usual LinkedIn groups that are the second home of the RPG Academy TechTip series!
LATEST COMMENTS
MC Press Online