1

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

由于 RPG 中的大多数时间字符串具有固定长度(至少对于我们的程序而言),而且也许更重要的是它们始终具有有限长度,所以当我想编写一个用于一般字符串操作的过程时,我总是有点迷茫。

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

我会发布我自己的尝试作为答案,但是那里有一些我不确定的问题。你是怎么做到的,因为我敢肯定很多人曾经或一千次做过这样的任务。不同的方法很受欢迎,最好是提到这些方法的问题。

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

4

2 回答 2

3

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));

于 2013-03-06T18:48:13.917 回答
0

我发现在 RPG 中最灵活的字符串传递方式适用于 64k-varlength 字符串并传递*varsize(它实际上应该只发送传递的字符串中的字节数,所以 64k 应该不是问题——我想我找到了斯科特克莱门特建议的某个地方)。在这里,我将如何编写一个仅限 AZ 的大写函数(因为它是一个最基本的示例):

 * 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 字节。但我认为这在理论上是我们程序的一个问题——至少目前是这样……
于 2013-03-06T16:57:49.377 回答