RPG中的多功能字符串操作程序

时间:2013-03-06 16:57:49

标签: string function procedure rpgle rpg

RPG中的另一件事我永远不知道如何正确行事:编写字符串操作函数/过程。

由于RPG中的大部分时间字符串都有固定的长度(至少在我们的程序中),也许更重要的是它们总是有一个有限的长度,当我想编写一般程序时,我总是有点迷失字符串操作。

如何编写一个处理任意长度的字符串的过程?有没有问题,如果我做功能样式(如text = manip_str(text);)?如果我操纵直接参数(如manip_str(text);),它是否可以用于不同的长度?

我会将自己的尝试作为答案发布,但那里有一些问题,我不确定。你是怎么做到的,因为我确信很多有一次或一千次这样的任务。如果你提到这些方法的问题,不同的方法会很好。

在您提出问题之前:对于(EBCDIC)字节字符串和(UTF-16)unicode字符串,我有这个问题。但我可以忍受两次手术,每次一次。

2 个答案:

答案 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 

现在有很多事情与我有关:

  • 我传递给固定长度字符串会有一些问题吗?
  • 这个“只传递所需的字节数”是否也适用于返回值?我不想每次都要保留数千个字节,并且每次我想要更新3个字符串字符串。
  • 它只能灵活到64k字节。但我认为这在理论上更符合我们的计​​划 - 至少现在......