RPG中的另一件事我永远不知道如何正确行事:编写字符串操作函数/过程。
由于RPG中的大部分时间字符串都有固定的长度(至少在我们的程序中),也许更重要的是它们总是有一个有限的长度,当我想编写一般程序时,我总是有点迷失字符串操作。
如何编写一个处理任意长度的字符串的过程?有没有问题,如果我做功能样式(如text = manip_str(text);
)?如果我操纵直接参数(如manip_str(text);
),它是否可以用于不同的长度?
我会将自己的尝试作为答案发布,但那里有一些问题,我不确定。你是怎么做到的,因为我确信很多有一次或一千次这样的任务。如果你提到这些方法的问题,不同的方法会很好。
在您提出问题之前:对于(EBCDIC)字节字符串和(UTF-16)unicode字符串,我有这个问题。但我可以忍受两次手术,每次一次。
答案 0 :(得分:3)
RPG中的大多数字符变量确实是固定长度。哪个表示有限长度。定义为50a的字符始终包含正好50个字符。 Eval myChar ='A';将导致myChar包含50个字符:字母A后跟49个空格。这很无聊但很重要。
第二个无聊但重要的一点是要理解调用者分配内存,而不是被调用者。如果调用者声明myChar 50a并且被调用者声明myParm 65535a,则调用者只初始化了50个字节的存储空间。如果被调用者尝试使用myParm超过第50个字节,则它正在处理条件未知的存储。正如他们所说,可能会出现不可预测的结果。
这是您关于子过程处理字符变量的问题的背景,其变量的大小事先不为子过程所知。处理此问题的经典方法是不仅传递字符变量,还传递其长度。 eval myProcedure(myChar:%len(myChar));这有点难看,它迫使每个来电者计算myChar的长度。如果子过程可以询问传入的参数以找出调用者如何定义它,那肯定会很好。
IBM通过他们称之为操作描述符的东西提供了这样的设施。使用操作描述符,调用者将有关字符参数的元数据传递给被调用者。一个人通过CEEDOD API检索。有一个使用CEEDOD here的例子。
基本上,子程序需要声明它需要操作描述符:
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));
答案 1 :(得分:0)
我发现RPG中最灵活的字符串传递方式是使用64k-varlength字符串并使用*varsize
传递(它实际上只发送传递的字符串中的字节数,所以64k不应该是个问题 - 我想我发现Scott Klement建议的地方。在这里我将如何编写一个只有A-Z的upcase函数(因为这是一个最基本的例子):
* typedefs:
Dstr_string_t S 65535A VARYING TEMPLATE
* constants:
Dstr_uc_letters_c C 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
Dstr_lc_letters_c C 'abcdefghijklmnopqrstuvwxyz'
* prototype:
Dstr_uc PR like(str_string_t)
D s like(str_string_t)
D options(*varsize) const
* implementation:
Pstr_uc B export
D PI like(str_string_t)
D s like(str_string_t)
D options(*varsize) const
/free
return %xlate(str_lc_letters_c:str_uc_letters_c:s);
/end-free
Pstr_uc E
现在有很多事情与我有关: