What exactly are BLANKS, ON, *OFF, etc? I see these used in RPG all the time and can usually tell what they mean, but I needed to know more details so here they are.
First, it is helpful to remember that string comparisons in RPG have a quirk compared to most languages on other platforms. When comparing strings of different lengths, the shorter string is right-padded with blanks (spaces) up to the length of the longer. Thus the two strings being compared are always equal-length. This means the two variable length strings varcharA = '';
and varcharB = ' ';
will compare as equal. Likely this quirk is due to its origins with only fixed-length (unterminated) strings available in the language.
Since this is a Python blog after all, let's see what this comparison would look like in Python:
import functools
@functools.total_ordering # fill in unimplemented comparison functions
class RpgStr:
def __init__(self, val: str = None):
"""Decorator for a normal python str, implementing RPG-like comparison function"""
self.val = val or ''
def __len__(self):
return len(self.val)
def __getattr__(self, item):
"""Pass-through to support other normal str functionality"""
return getattr(self.val, item)
def __eq__(self, other):
return self.ljust(len(other)) == other.ljust(len(self))
def __lt__(self, other):
return self.ljust(len(other)) < (other.ljust(len(self)))
a = RpgStr('abc')
b = RpgStr('abc' + ' ')
print(a == b) # True
Knowing the above regarding string comparisons makes it easy to understand how these "figurative constants" work in RPG. Each represents a number or character set that will be repeated to build a string for use in comparisons and assignments. The length of the constant will be equal to the length of the other "side" of the comparison or assignment.
Figurative Constants
Here are the available figurative constants:
-
*ALL'x...'
: repeats the characters in the single-quotes up to the specified length. Variations*ALLG
,*ALLU
, and*ALLX
exist for repeated graphic, unicode, and hexadecimal literals. -
*BLANK
/*BLANKS
: repeats a blank (' ') character up to the specified length, usually equivalent to*ALL' '
. Valid only for string types. -
*ZERO
/*ZEROS
: repeats a zero ('0') character up to the specified length, usually equivalent to*ALL'0'
. -
*HIVAL
,*LOVAL
: represents the maximum or minimum value representable by a numeric or date/time type. examples99
and-99
for typepacked(2,0)
. Interestingly, the lo/hi value for date/times may vary based on the settings for the DATFMT or TIMFMT. -
*NULL
represents a null value for pointers (not the same as NULL in SQL) -
*ON
and*OFF
map to'1'
and'0'
respectivally and are valid for character types and indicators (indicators are essentially char(1) values which must be'1'
or'0'
)
Note: there is no difference between the singular and plural forms (e.g. *BLANK
and *BLANKS
); they are synonyms for convenience
Examples for fun
The following code is ready to compile and call:
**free
dcl-s vStr varchar(10) inz('');
dcl-s vInitBlankStr varchar(10) inz(*blanks); // will have %len() = 10
dcl-s vLongStr varchar(24) inz('');
dcl-s vTenDigit packed(10) inz(0);
dcl-s vStrArray varchar(10) dim(10);
dcl-s vPrompt varchar(25) inz('Press Enter');
dcl-s vResult varchar(1) inz('');
// compare empty string to *blanks, *blanks will be ''
if vStr = *blanks;
dsply 'vStr = *blanks evals TRUE, with length 0';
endif;
// compare long blank string to *blanks, *blanks will be ' '
if vInitBlankStr = *blanks;
dsply 'vInitBlankStr = *blanks evals TRUE, with length 10';
endif;
// compare different-length strings. shortest will be padded
if vStr = vInitBlankStr;
dsply 'vStr = vInitBlankStr evals TRUE, with diff lengths';
endif;
// assign blanks to a variable string type, will be padded to the max size
vStr = *blanks;
dsply ('length of vStr: ' + %len(vStr)); // this will display 10
// assign a repeating sequence to a str
vLongStr = *ALL'xyzzy';
dsply ('vLongStr = ' + vLongStr); // 'xyzzyxyzzyxyzzyxyzzyxyzz'
// assign repeating digits to a number
vTenDigit = *ALL'72';
dsply ('vTenDigit = ' + vTenDigit); // '7272727272'
// can I turn a string to all ones?
vStr = *ON;
dsply vStr; // yep; '1111111111'
// fill an entire array with a figurative constant?
vStrArray = *ALL'123';
dsply ('vStrArray(2) = ' + vStrArray(2)); // each array value is now '1231231231'
*inlr = *on;
dsply vPrompt '' vResult; // pause at the end of execution
Running the example Code
RPG code runs on IBM i
systems. Paste the above code in a source member called CHKCONST
in <yourlib>/QRPGLESRC
and use the following to compile/run:
Compile:
CRTBNDRPG PGM(<yourlib>/CHKCONST) REPLACE(*YES)
Run:
CALL <yourlib>/CHKCONST
Recommendations
After looking at many examples of these constants in use, and playing with them, I have these recommendations:
-
Take advantage of checking whether a string is "empty" with
vStr = *blanks
, whether the string length is variable or fixed! In other languages, we often must write some variation of%rtrim(vStr) = ''
and the "RPG way" is cleaner. -
Remember that assigning
*blanks
to a variable length string will waste memory. UsevStr = ''
for a zero-length string. You can use this for fixed-length strings too, and it will still blank the entire string. -
If you need to compare BOTH length and content of two strings, you must explicitly do so:
if %len(string1) = %len(string2) and string1 = string2
. Putting the length comparison first is likely best for both readability and short-circuit efficiency. -
In free-form (modern) RPG, I see no reason to use
*ZERO
for numeric types, as opposed to just a literal0
. -
When setting indicator variables, always use
*ON
or*OFF
as the value (never the literals1
or0
). This will help future-proof your code. -
If you are using comparisons to
*ON
or*OFF
you can stop:if *in45 = *ON;
is the same asif *in45;
. In fact the expression*in45 = *ON
internally evaluates one of the literals*ON
or*OFF
. You can use this in assignments: after the statementisAwesome = ('RPG' = 'Awesome')
the indicator/charisAwesome
will contain*OFF
.