RPG 中的大多数字符变量确实是固定长度的。这意味着有限的长度。定义为 50a 的字符将始终恰好包含 50 个字符。评估 myChar = 'A'; 将导致 myChar 包含 50 个字符:字母 A 后跟 49 个空格。这很无聊但很重要。
第二个无聊但重要的一点是要了解调用者分配内存,而不是被调用者。如果调用者声明 myChar 50a 而被调用者声明 myParm 65535a,则调用者只初始化了 50 个字节的存储空间。如果被调用者尝试使用 myParm 超过第 50 个字节,则它正在使用条件未知的存储。正如他们所说,可能会出现不可预知的结果。
这是您关于处理字符变量的子过程的背景问题,该字符变量的大小事先不为子过程所知。处理这个问题的经典方法是不仅传递字符变量,而且传递它的长度。eval myProcedure(myChar: %len(myChar)); 这有点难看,它迫使每个调用者计算 myChar 的长度。如果子过程可以询问传入参数以找到调用者如何定义它,那肯定会很好。
IBM 通过他们称为操作描述符的东西提供了这样的工具。使用操作描述符,调用者将有关字符参数的元数据传递给被调用者。一个通过CEEDOD API 检索它。这里有一个使用 CEEDOD 的例子。
基本上,子过程需要声明它需要操作描述符:
dddeCheck pr n opdesc
d test 20a const options(*varsize)
然后调用者对子过程进行正常的调用:
if ddeCheck(mtel) = *on; // 10 bytes
...
endif;
if ddeCheck(mdate: *on) = *on; // 6 bytes
...
endif;
请注意,调用者将不同大小的固定长度变量传递给子过程。
子过程需要使用 CEEDOD 来询问传入参数的长度:
dddeCheck pi n opdesc
d test 20a const options(*varsize)
...
dCEEDOD pr
d parmNum 10i 0 const
d descType 10i 0
d dataType 10i 0
d descInfo1 10i 0
d descInfo2 10i 0
d parmLen 10i 0
d ec 12a options(*omit)
d parmNum s 10i 0
d descType s 10i 0
d dataType s 10i 0
d descInfo1 s 10i 0
d descInfo2 s 10i 0
d parmLen s 10i 0
d ec s 12a
...
CEEDOD (1: descType: dataType: descinfo1: descinfo2: parmlen: *omit);
此时,parmlen 包含调用者将传入变量定义为的长度。现在由我们来处理这些信息。如果我们正在逐个字符处理,我们需要做这样的事情:
for i = 1 to parmLen;
char_test = %subst(test: i: 1);
...
endfor;
如果我们作为单个字符串进行处理,我们需要执行以下操作:
returnVar = %xlate(str_lc_letters_c: str_uc_letters_c: %subst(s: 1: parmLen));
重要的是永远不要引用输入参数,除非该引用以某种方式受到调用者定义的实际可变长度的限制。这些预防措施仅对固定长度变量是必需的。编译器已经知道变长字符变量的长度。
关于编译器通过 CONST 将 myFixed 映射到 myVarying 的方式,了解它是如何工作的。编译器会将 myFixed 中的所有字节复制到 MyVarying 中——所有这些字节。如果 myFixed 为 10a,则 myVarying 将变为 10 个字节长。如果 myFixed 为 50a,myVarying 将变为 50 字节长。始终包含尾随空格,因为它们是每个固定长度字符变量的一部分。这些空白对于翻译过程并不重要,忽略空白的过程,但它们对于将字符串居中的过程可能很重要。在这种情况下,您需要求助于操作描述符或执行类似的操作upperVary = str_us(%trimr(myFixed));