Deck: Handling quotes in CL concatenation
Handling character strings can be confusing in CL, especially when you're splicing several pieces together with the concatenation operators. One has only to browse through the AS/400 message base in OpenBBS to realize that this topic confuses many would-be OPNQRYF users.
In this article, I'll break down string manipulation in CL in order to do away with the mystery and so you can exploit OPNQRYF to your benefit. Throughout this text I'll call the ' character a single quote (which is also referred to as an apostrophe).
Simple Strings
As long as there are no embedded single quote characters in the strings, you'll never have a problem with them. For example:
CHGVAR VAR(&TITLE) VALUE('Quote and Unquote')
The CHGVAR command listed above assigns the value 'Quote and Unquote' to variable &TITLE. However, most assignments are more complicated and usually involve the concatenation and substring functions.
This type of assignment, however common, doesn't take advantage of the concatenation operators or the substring function. Let's define them briefly:
*CAT: Takes two strings and concatenates them, or pastes them together. For example:
DCL VAR(&FIRST) TYPE(*CHAR) LEN(10) VALUE('Johnny') DCL VAR(&LAST) TYPE(*CHAR) LEN(15) VALUE('Weissmuller') DCL VAR(&NAME) TYPE(*CHAR) LEN(30) CHGVAR VAR(&NAME) VALUE(&FIRST *CAT &LAST)
The CHGVAR command assigns the value 'Johnny Weissmuller' to variable &NAME, including five blanks in the middle. This is so because *CAT concatenates the variables in their entirety. &FIRST is 10 characters long, but only the first six characters are used. Too bad! *CAT includes all 10 characters.
*BCAT: Concatenates two strings, (and leaves just a single blank in between). To do this, all trailing blanks are removed from the first value, and then a single blank space is placed in the middle. Changing the above example to use *BCAT,
CHGVAR VAR(&NAME) VALUE(&FIRST *BCAT &LAST)
assigns the value 'Johnny Weissmuller' (with only one blank in the middle) to &NAME. That seems more sensible.
*TCAT: Concatenates two strings, removing all trailing blanks from the first string and placing no blanks in between. For example:
DCL VAR(&LIB) TYPE(*CHAR) LEN(10) VALUE('QGPL') DCL VAR(&FILE) TYPE(*CHAR) LEN(10) VALUE('QCLSRC') DCL VAR(&QUAL) TYPE(*CHAR) LEN(21) CHGVAR VAR(&QUAL) VALUE(&LIB *TCAT '/' *CAT &FILE)
Variable &QUAL will have the value 'QGPL/QCLSRC' (without any embedded spaces), which is how it should be. Notice that the second *CAT could have been replaced with another *TCAT without changing the result.
%SST: Substring function, used to extract a portion of a character string. %SST must be immediately followed by an opening parenthesis, three values separated by spaces and a closing parenthesis. The first value is the original string from which we'll do the extracting. The second value indicates in what position to start extracting (byte number). The third value indicates how many characters to extract. Example:
DCL VAR(&TITLE) TYPE(*CHAR) LEN(20) + VALUE('QUOTE UNQUOTE') DCL VAR(&PORTION) TYPE(*CHAR) LEN(5) CHGVAR VAR(&PORTION) VALUE(%SST(&TITLE 2 6))
Variable &PORTION will have the value 'UOTE U' (six characters beginning with the second character).
Back to Embedded Quotes
Nothing prevents a character string from containing a single quote character somewhere. Try the following example of the CHGVAR command on for size:
CHGVAR VAR(&PHRASE) VALUE('It''s here')
Golden Rule Number One: When surrounding a character string with single quotes, you must double up any single quotes embedded in the string. The string we want to assign to variable &PHRASE is simply the following:
It's here
You can also use the double quote character instead of two consecutive single quotes. Thus, the CHGVAR command could have been coded as follows:
CHGVAR VAR(&PHRASE) VALUE('It"s here')
IBM manuals often use the single and double quotes next to one another, like this:
CHGVAR VAR(&TITLE) VALUE('Don"' *CAT 't do this.')
Personally, I think this confuses more than it helps. It confuses because it introduces a character as a synonym of two others. But, you should use whatever method makes more sense to you.
Comparing Variables
You can easily compare two variables using the IF command's COND parameter. When performing a comparison, both sides of the comparison operator (*EQ, *NE, etc.) must be of the same type; that is, both must be character or both must be decimal. Here are two examples:
IF COND(&STATE *EQ 'CA') THEN(...)
IF COND(&ZIP *EQ 92008) THEN(...)
Variable &STATE is of type character, so it must be compared to a character string like 'CA' (always surrounded by single quotes). Variable &ZIP is of type decimal, so it must be compared to a decimal value like 92008, which by not having single quotes, is automatically considered decimal.
OPNQRYF and Quotes
OPNQRYF has a parameter, QRYSLT, where you must code a character string that represents what records are to be selected from the database file.
In contrast, the IF command's COND parameter doesn't use a character string, but an actual logical expression consisting of (at least) two values separated by a comparison operator such as *EQ.
OPNQRYF, let me emphasize it, uses a single character string in the QRYSLT parameter, meaning that whatever you code in the QRYSLT parameter must be enclosed in single quotes.
Let's try to code the QRYSLT parameter to select all records that have a state of California and a ZIP code of 92008:
OPNQRYF FILE(...) QRYSLT('STATE *EQ CA *AND ZIP *EQ 92008')
This quickly produces an error message, because the first *EQ tries to compare field STATE to field CA. That's right--OPNQRYF assumes that CA is the name of a field because it's not surrounded by single quotes. So let's try this:
OPNQRYF FILE(...) QRYSLT('STATE *EQ 'CA' *AND ZIP *EQ 92008')
This produces a different error message, even sooner, because we're breaking Golden Rule Number One. The single quotes surrounding CA must be doubled, as in:
OPNQRYF FILE(...) QRYSLT('STATE *EQ ''CA'' *AND ZIP *EQ 92008')
Notice that the second *EQ has never been in error; we're comparing field ZIP and the decimal constant 92008.
Representing a Single Quote
Many times it becomes necessary to concatenate a single quote to longer strings. How do you code a single quote constant in CL?
Because it's going to be a character string, it must be enclosed in single quotes, so it's only natural to think that it would be:
'''
Doing this, however, violates Golden Rule Number One. The single quote must be doubled because it's surrounded by single quotes, so you end up with the following:
''''
That's how you code a single quote, believe it or not. It makes sense, if you think about it. For simplicity, I recommend you create a variable, "E, that contains this value:
DCL VAR("E) TYPE(*CHAR) LEN(1) VALUE('''')
This DCL command declares variable "E as a one-byte character string, and it initializes it to a single quote. From then on you can simply use variable "E anywhere you need a single quote. We'll use this variable in the next section.
Using Variables in QRYSLT
OPNQRYF provides much flexibility by allowing variables and constants to be concatenated in the QRYSLT parameter. When you combine variables and constants, you'll have to concatenate them since--I repeat--since QRYSLT must have a single character string only.
Let's say that, in order to add flexibility, we want to change the previous QRYSLT parameter so that the state and the zip code are given in variables. The OPNQRYF command, then, will select all the records of the database file that have the state and the zip code that match those you enter in the variables:
DCL VAR(&STATE) TYPE(*CHAR) LEN(2) DCL VAR(&ZIP) TYPE(*DEC) LEN(5 0) DCL VAR(&ZIPCHAR) TYPE(*CHAR) LEN(5) DCL VAR("E) TYPE(*CHAR) LEN(1) VALUE('''') CHGVAR VAR(&ZIPCHAR) VALUE(&ZIP) OPNQRYF FILE(...) QRYSLT('STATE *EQ' *BCAT "E + *CAT &STATE *CAT + "E *BCAT '*AND ZIP *EQ' *BCAT &ZIPCHAR)
Assume we've entered a value of 'NY' for &STATE and a value of 10012 for &ZIP. Somehow we need to concatenate the ZIP code of 10012 (which is a decimal value) to the rest of the string. Since decimal values can't be concatenated, we first convert it to character with the CHGVAR command, receiving the value of &ZIP (decimal variable) into &ZIPCHAR (character value).
Let's analyze the QRYSLT string now. I'll pull out the first half and start evaluating it, pretending that I'm the computer. Each consecutive line has an intermediate result after each evaluation. The last line contains the final string shown in 1.
Let's analyze the QRYSLT string now. I'll pull out the first half and start evaluating it, pretending that I'm the computer. Each consecutive line has an intermediate result after each evaluation. The last line contains the final string shown in Figure 1.
The second half evaluates even more easily:
'*AND ZIP *EQ' *BCAT &ZIPCHAR
*AND ZIP *EQ 10012
*AND ZIP *EQ 10012
Putting them together, we obtain the final QRYSLT string:
STATE *EQ 'NY' *AND ZIP *EQ 10012
Quadruple Quotes
If you think that doubling quotes is where it all ends, think again. Quotes can be quadrupled, as many S/38 programmers using SBMJOB with RQSDTA will confirm.
As an example, suppose you want to send the system operator the following message:
I'm here!
Paying tribute to Golden Rule Number One, you'd code the SNDMSG command as follows:
SNDMSG MSG('I''m here!') TOMSGQ(QSYSOPR)
But wait. Now we need to submit this job to batch, which is done with the SBMJOB command. You can use either the CMD or the RQSDTA parameters. The difference is that the CMD parameter expects a command string, while RQSDTA expects a character string (there we go again!).
Let's use the RQSDTA parameter. We'll need to enclose the entire command string in single quotes in order to make it a character string. According to Golden Rule Number One, all single quotes are doubled. Guess what happens when you already have doubled single quotes? You've got it--they're doubled again:
'SNDMSG MSG(''I''''m here!'') TOMSGQ(QSYSOPR)'
Therefore, the SBMJOB command looks like this, shown in 2.
Therefore, the SBMJOB command looks like this, shown in Figure 2.
Octuple Quotes
When doubling quotes time after time, only the sky's the limit. If you wanted to run the above SBMJOB command from within an RPG program, you'd have to use QCMDEXC to run the command.
QCMDEXC requires the command string as a character string in its first parameter. Well, here we go again! The first parameter for QCMDEXC would be as shown in 3.
QCMDEXC requires the command string as a character string in its first parameter. Well, here we go again! The first parameter for QCMDEXC would be as shown in Figure 3.
I suspect there are cases when you'd need 16 or 32 single quotes in a row. If you find one, give yourself a pat on your back. As teachers love to say, I'll leave that as an exercise for the student.
Quote...unquote: Help with CL String Manipulation
Figure 1 Evaluation of QRYSLT string
Figure 1: Evaluation of QRYSLT string 'STATE *EQ' *BCAT "E *CAT &STATE *CAT "E STATE *EQ ' NY ' STATE *EQ 'NY'
Quote...unquote: Help with CL String Manipulation
Figure 2 Example of quadruple quotes
Figure 2: Example of Quadruple Quotes SBMJOB RQSDTA('SNDMSG MSG(''I''''m here!'') TOMSGQ(QSYSOPR)') + JOBQ(QBATCH)
Quote...unquote: Help with CL String Manipulation
Figure 3 Example of octuple quotes
Figure 3: Example of Octuple Quotes 'SBMJOB RQSDTA(''SNDMSG MSG(''''I''''''''m here!'''') TOMSGQ(QSYSOPR)'') JOBQ(QBATCH)'
LATEST COMMENTS
MC Press Online