A Bevy of BIFs: %XLATE and %REPLACE
March 4, 2009 Jon Paris
The %XLATE and %REPLACE text replacement built-in functions (BIFs) are often confused, and part of that confusion may be the result of wishful thinking on the part of RPGers frustrated with the limitations of %XLATE. Just as with the %SCAN and %CHECK BIFs that I discussed in an earlier tip, the major difference between %XLATE and %REPLACE is that %XLATE operates on individual characters and %REPLACE operates on strings. Before we study %REPLACE, let’s quickly review the function of %XLATE. %XLATE( from : to : input {: startpos} ) The function of %XLATE is to search the input string for any instances of the individual characters contained in the from string and to replace them with the corresponding character in the to string. So in this example: result = %XLATE( 'ABC': 'XYZ': source); Any occurrences of the character ‘A’ in the source string will be replaced by ‘X’, ‘B’ by ‘Y’, and ‘C’ by ‘Z’. So if we assume that source contains ‘123ABC789’ before the operation, afterward the result will contain ‘123XYZ789’. The mistake that many programmers make is to think that %XLATE is replacing the combination of characters. In other words, in the above example, the string ‘ABC’ is being replaced. It is, but only as the result of the characters ‘A’, ‘B’, and ‘C’ being individually replaced. Had the input contained ‘ABD’, it would have been converted to ‘XYD’. This misconception can result in code being written that will “almost” work and that may under test conditions appear to work correctly. Suppose that we want to replace any occurrence of the character string ‘/*’ with ‘&&’. The programmer might code: result = %Xlate('/*': '&&': source); But while this may appear at first glance to work, it will not only convert all instances of ‘/*’ but, because it is operating on a character by character basis, it will also translate individual ‘/’ or ‘*’ characters–probably not what was wanted. The following code demonstrates this: D source s 36a D result s 36a D from s 5a Inz('/*') varying D to s 5a Inz('&&') varying /Free // Demonstrate effect of using the %Xlate approach // - Results in '&& Leave &these& and &these& &&' source = '/* Leave /these/ and *these* /*'; dsply ('Source: ' + source); result = %Xlate(from: to: source); dsply ('Result:' + result); While it would be nice if RPG did indeed have a built-in function that operated as desired (i.e., to replace one string with another), it actually requires a combination of the %SCAN and %REPLACE BIF’s to achieve this. The basic syntax for %REPLACE is shown here: %REPLACE( replacement : source { :startpos {: length to replace}} ) The code below demonstrates how these BIFs can be used in combination to achieve the desired functionality. The start position for the string to be replaced is obtained by an initial %SCAN (A). It is then used to specify the start position for the %REPLACE operation (B). On the subsequent scan operations, the optional start parameter (C) is used to skip past the portion of the string that has already been processed. D result s 36a D from s 5a Inz('/*') varying D to s 5a Inz('&&') varying D position s 5i 0 D start s 5i 0 /Free // This is a valid approach to handling character strings // - It will result in '&& Leave /these/ and *these* &&' result = '/* Leave /these/ and *these* /*'; (A) position = %Scan( from: result); DoW position > 0; (B) result = %Replace( to: result: position); start = position + 1; (C) position = %Scan( from: result: start); EndDo; dsply ('Result:' + result); While %REPLACE may not operate exactly as we’d have liked it to, it does have a number of nice features. For one, the length of the replacement string does not have to match the length of the text to be replaced. In such cases, the optional fourth parameter is used to specify the number of characters to be replaced. This makes the BIF very useful in mail-merge type operations where you want to replace a text marker with a variable amount of text. For example, the base text might include a marker like “&Name”, which we are going to replace with the customer’s name. So we might code the %REPLACE operation like this: result = %Replace( custName: result: startPosition: 5); This will cause the BIF to replace “&Name” (five characters) with the content of custName. Once again variable length fields are useful with this BIF. In fact, the above code assumes that custName is a variable length field; if it were not we would have had to code %TrimR(custName) as the parameter to avoid inserting potentially large numbers of blanks. There is a lot more we could say about %REPLACE, but the intent of this tip is to clarify the differences between %XLATE and %REPLACE. Perhaps we’ll return to the topic on another occasion. Jon Paris is one of the world’s most knowledgeable experts on programming on the System i platform. Paris cut his teeth on the System/38 way back when, and in 1987 he joined IBM’s Toronto software lab to work on the COBOL compilers for the System/38 and System/36. He also worked on the creation of the COBOL/400 compilers for the original AS/400s back in 1988, and was one of the key developers behind RPG IV and the CODE/400 development tool. In 1998, he left IBM to start his own education and training firm, a job he does to this day with his wife, Susan Gantner–also an expert in System i programming. Paris and Gantner, along with Paul Tuohy and Skip Marchesani, are co-founders of System i Developer, which hosts the new RPG & DB2 Summitconference. Send your questions or comments for Jon to Ted Holt via the IT Jungle Contact page. RELATED STORIES A Bevy of BIFs: %SCAN and %CHECK A Bevy of BIFs: Getting a Date is Easy with %Date A Bevy of BIFs: Look Up to %LookUp
|
How will I convert XXXLIB/QSQLSRC.SQL1 to XXXMOCK.QSQLSRC(SQL1) ?