Guru Classic: A Bevy of BIFs — %CHAR, %EDITC and %EDITW
February 13, 2019 Jon Paris
Author’s Note: The reason I chose this particular tip to revisit was that a similar question came up on one of the RPG web forums the other day. The questioner was looking for an easy way to edit a numeric field to include the use of colons as separators. Because the answer involves the use of %EditW I have added a simple example of its use at the end of this tip.
“I need to convert a numeric value into a character string. How do I . . . ?” This question is often asked by RPGers as they face having to produce CSV files, XML and JSON documents, or Web pages (HTML). Generating such data has become a common requirement in the lives of many RPGers.
I’ll start by taking a quick look at one of my most frequently used built-in functions: %Char. Originally introduced to handle conversion of date fields to a character form, %Char was quickly upgraded for use with numeric fields and expressions. But the way in which it operates catches many programmers by surprise. Why? Because %Char performs leading zero suppression and then trims the resulting string to remove leading and trailing blanks. As a result, the length of the resulting string varies depending on the value in the field being converted. As we will see later, this is very different from %EDITC (and its companion %EDITW), which will always produce a consistent-length output from a given input field definition and edit code combination, regardless of the actual content of the field.
As a result, when you run the code below, the first DSPLY message (A) will say “%Char value is (12.34)” and the second (B) “%Char value is (10012.34)”. When you are building text such as that used in the message here, this is a good thing. It saves you having to worry about stripping the blanks etc. It will even place a minus sign in front of negative numbers and, as you can see, will insert a decimal point when the expression contains decimal places. Very useful.
But what if you want to simply convert a numeric value to its character equivalent? For example, to replace an old-fashioned MOVE operation. If you try to use %Char for that purpose you will be disappointed for many reasons. For one you may find an unwanted decimal point inserted as in our examples, and for another, because you’ll either end up with trailing spaces in the output field (C) “Value of 12.34 results in (12.34 )”, or worse still, truncation of the decimal places (D) “Value of 10012.34 results in (10012.3)” and that’s probably not what you expect, or indeed want.
Dcl-s testNum packed(7:2) inz(12.34); Dcl-s testChar char(7); Dcl-s phoneNum packed(11) inz(2535551234); // Effect of %Char when building a string Dsply ('Showing effect of %Char in string build'); (A) Dsply ('%Char value is (' + %Char(testNum) + ')'); testNum += 10000; (B) Dsply ('%Char value is (' + %Char(testNum) + ')'); testNum = 12.34; // Reset to original value // Attempt to use %Char() as equivalent to MOVE Dsply ('Showing effect of %Char as replacement for MOVE'); testChar = %Char(testNum); (C) Dsply ('Value of ' + %Char(testNum) + ' results in (' + testChar + ')'); testNum += 10000; testChar = %Char(testNum); (D) Dsply ('Value of ' + %Char(testNum) + ' results in (' + testChar + ')'); testNum = 12.34; // Reset to original value
Replacing the MOVE Operation
So how do you handle the requirement to replace a MOVE operation? The answer is %EditC. %EditC applies an edit code to a numeric field just as it would if you used that code on an O-spec or in a DDS definition. It then returns you the edited character string. So what’s the magic code that allows us to insert leading zeros and not have to worry about decimal place insertion etc? ‘X’ marks the spot! The X edit code will retain leading zeros, and will not insert a decimal point, comma, or anything else. The result is that our test program will output (E) “Value of 12.34 results in (0001234)” and (F) “Value of 10012.34 results in (1001234)”. Note that the length of the display string is identical in each case. Any negative values will be indicated by an alpha character in the right-most position of the field just as they would have been with a MOVE.
We won’t go into details of all of the edit code options available. As we noted earlier they are identical to those used on O-specs and in DDS. Having said that, there is one difference between the way certain editing requirements are specified with %EditC relative to O-specs. If you require the resulting string to have leading zeros replaced by asterisks, then the keyword *ASTFILL should be specified as the third parameter to %EditC. Similarly, if you want a floating currency sign to be added to the field, you can specify *CURSYM for the default value, or enter a single character of your own choosing to have that act as the floating currency symbol.
Using %EditW for Even More Options
Just as with %EditC, the format of the edit words used by %EditW is exactly the same as on O-specs and DDS. To round out this tip, I have added a couple of examples of using %EditW, one of which is similar to the requirements of the original questioner to insert colon separators into a numeric field.
The first example (G) applies the edit word ‘0(bbb)&bbb-bbbb’ (where the letter “b” represents a blank) to a numeric field intended to represent a phone number. The resulting display looks like this: “Edited 2535551234 as phone number: (253) 555-1234”. This demonstrates a slightly annoying feature of edit words in that the field has to be specified as one digit longer than we really need it to be (11 digits and not 10) in order that we can specify a “0” (zero) in the string ahead of the “(“. The rule is that there has to be a digit from the field ahead of the first insertion character. Annoying but there it is.
The second example (H) simply breaks the number up into pairs of digits separated by colons using the edit word ‘b0b:bb:bb:bb:bb’ resulting in the display “Simple colon insertion results in: 25:35:55:12:34”.
// Now use %EditC as substitute for MOVE Dsply ('Showing effect of %EditC as replacement for MOVE'); testChar = %EditC(testNum: 'X'); (E) Dsply ('Value of ' + %Char(testNum) + ' results in (' + testChar + ')'); testNum += 10000; testChar = %EditC(testNum: 'X'); (F) Dsply ('Value of ' + %Char(testNum) + ' results in (' + testChar + ')'); // Simple examples of %EditW Dsply ('Showing effect of using %EditW'); (G) Dsply ('Edited ' + %Char(phoneNum) + ' as phone number:' + %EditW(phoneNum: '0( )& - ') ); (H) Dsply ('Simple colon insertion results in:' + %EditW(phoneNum: ' 0 : : : : ') ); *InLR = *On;
As you can see, there’s a lot you can do with these BIFs. If you have been using O-specs or other techniques to format this kind of data, perhaps it is time to revise your coding techniques.
Jon Paris is one of the world’s foremost experts on programming on the IBM i platform. A frequent author, forum contributor, and speaker at User Groups and technical conferences around the world, he is also an IBM Champion and a partner at Partner400 and System i Developer. He hosts the RPG & DB2 Summit twice per year with partners Susan Gantner and Paul Tuohy.