IBM Support

RPG近期新功能介绍(1)

Technical Blog Post


Abstract

RPG近期新功能介绍(1)

Body

 

V5R1以来,针对用户的诸多反馈,RPG逐步在IBM i操作系统的每个版本里都增加了一些新功能。有针对返回大数据量结果集的提升性能的改进;有针对程序可读性和易维护性方面的各种新函数的推出;也有提高效率,简化程序逻辑的新语法的引入;还有配合XML的各种新的操作。本文将陆续推出一系列的介绍,将各种RPG的新功能讲解给大家。

 

l        文件读写时使用数据结构的支持

V5R2 之后,RPG开始支持在I/O中直接使用数据结构,这个功能一方面简化了RPG代码,增强了程序的可读性,同时它还能带来程序执行效率上的提高。

CHAIN来说,数据结构用法如下:

Free-Form Syntax

CHAIN{(ENHMR)} search-arg name {data-structure}

 

Code

Factor 1

Factor 2

Result Field

Indicators

CHAIN (E N)

search-arg

name (file or record format)

data-structure

NR

ER

_

RPG代码中可这样使用:

D DataIn       ds                  likerec(Recfmt:*input)

 

 /free

     chain key Recfmt DataIn;

if (DataIn.op = *BLANKS);

    do something here

endif;

 

不用数据结构的代码可能如下:

 /free

     chain key Recfmt ;

if (op = *BLANKS);

    // do something here

endif;

在上面的代码中,变量op 是否是来自记录Recfmt,不如前个例子明显。相比而言,程序可读性差了些。

而效率的提高,主要是因为在IO中使用数据结构之后,数据将直接从文件中传到数据结构中。这也会导致字段指示器(field indicators)在IO过程中不被设置。

那么怎么定义这些IO操作可以使用数据结构呢?

如果IO中涉及程序描述文件(program described file),则要求数据结构的长度跟文件的记录长度相同。

如果IO中涉及外部描述文件(externally described file),则使用以下的规则:

1. 对于CHAIN, READ, READC, READE, READP, READPE中用到的数据结构, 必须在D 表中用LIKEREC(rec:*INPUT) 或者 EXTNAME(file:rec:* INPUT)来定义。需要注意的是用LIKEREC定义的数据结构默认是qualified的,即必须用ds.field方试访问。

2. 对于UPDATE中用到的数据结构可以在D 表中用LIKEREC(rec:*INPUT) 或者 EXTNAME(file:rec:* INPUT)定义,也可以用LIKEREC(rec:*OUTPUT) 或者 EXTNAME(file:rec:* OUTPUT)定义。

3. 对于WRITE中用到的数据结构, 必须在D 表中用LIKEREC(rec:*OUTPUT) 或者EXTNAME(file:rec:* OUTPUT)定义。

4.对于EXFMT中用到的数据结构(这是V6R1新增的功能),必须在D 表中用LIKEREC(rec:*ALL) 或者EXTNAME(file:rec:* ALL)定义。

5.也可以用LIKEDS(ds)来定义,但ds必须是以上面的规则定义的数据结构。

 

以下是一个来自info center 的例子:

* DDS for display file MYDSPF

A          R REC

A            QUESTION      40A  O  5  2

A            NAME          20A  I  7  5

A            CITY          20A  B  8  5

 

 * RPG program using MYDSPF

Fmydspf    cf   e             workstn       

 * Define a data structure for use with EXFMT REC

D recDs           ds                  likerec(rec : *all)

 /free    

        // Set the output-capable fields

        recDs.question = 'What is your name?';

        recDs.city = 'Toronto';

        // Show the screen to the user

        exfmt rec recDs;           

        // Use the input-capable fields

        dsply ('Hello ' + recDs.name + ' in ' + recDs.city); 

 

但是对一个表(disk file),同一个数据结构既要做输入,又要做输出呢?有一个方法可以实现:

 

D MyData         ds                  qualified

D   In                                likerec(Recfmt:*input)

D   Out                              likerec(Recfmt:*output)

D                                     overlay(In)

 

 /free

     read  Recfmt MyData.In;

     // do some stuff

     MyData.In.Name = 'zhang san';

     MyData.In.Id = 'ID';

     write Recfmt MyData.Out;

 

l        对别名(Alias)的支持

6.1起,RPG引入了对别名(Alias)的支持。IBM i数据库本身从很久以前(可以追溯到System38的时代)就支持使用一个最大长度为128个字符的别名作为alternative name,从而提供另一种更清晰,可读性更好的方式来引用至多10个字符的标准字段名。而对于RPGIII,仅支持6位字符,以至于我们有些老客户还在沿用含义并不清晰的字段名如CUSTNM,CUSTAD。在6.1以前,只能对这些字段使用alternate name以提高可读性,并在SQL中引用。如CUSTOMER_NAME,CUSTOMER_ADDRESS。

现在,我们可以在RPG中,使用新的关键字ALIAS引用alternate name来替代那些只有10位长甚至更短的标准字段名了。

例如,我们有这样一个PF CUSTFILE,DDS定义如下:

     A          R CUSTREC

     A            CUSTNM        25A         ALIAS(CUSTOMER_NAME)

     A            CUSTAD        25A         ALIAS(CUSTOMER_ADDRESS)

     A            ID            10P 0

RPG的Definition Specification(D表)中,使用关键字ALIAS来标明,外部引用的数据结构中,使用alternate name来定义该结构的子字段名。

示例如下:

     D custDs        e ds                  ALIAS

     D                                     QUALIFIED EXTNAME(custFile)

      /free

        custDs.customer_name = 'John Smith';

        custDs.customer_address = '123 Mockingbird Lane';

        custDs.id = 12345;

也可以在RPG的File Description Specification(F表)中,使用关键字ALIAS,然后D表中使用LIKEREC关键字声明的相关数据结构将使用alternate name来定义该结构的子字段名。

示例如下:

     FcustFile   IF  E           K Disk    ALIAS QUALIFIED

 

     D myDs            ds                  LIKEREC(custFile.custRec)

      *

/free

read custFile myDs;

if myDs.customer_name <> *blanks

and myDs.id > 0;

...

 

 

l        新的内置函数:%SCANRPL

7.1中,RPG引入了一个新的内置函数(Build-in Function):%SCANRPL。在这个函数出现之前,我们想替换字符串中的子串时,必须结合使用%SCAN和%REPLACE这两个内置函数。如果想多次重复替换同一字串,还要加上循环语句。%SCANRPL的出现,使得这种程序逻辑被简化了很多。比如:我们有一个字符串String1,其内容为'&Name1 is &Name2's father, &Name2 is &Name1's son. ',我们用'Tom'来替换String1中的'&Name1',只需要下面的代码就能完成:

string1 = '&Name1 is &Name2''s father, &Name2 is &Name1''s son. ';

string2 = %ScanRpl('&Name1' : 'Tom' : string1);

此时,即使'Tom'和'&Name1'的长度不等,string1中的两处&Name1也将被全部替换成Tom。结果如下:

// string2 = 'Tom is &Name2''s father, &Name2 is Tom''s son. '

%SCANRPL还提供了更为灵活的两个可选参数,通过这两个可选参数,我们可以指定要搜索的子串的起始位置和搜索的长度。

比如:

string3 = %ScanRpl('&Name2' : 'Jack' : string2 : 9);

结果为:

// string3 = 'Tom is &Name2''s father, Jack is Tom''s son. ',只有第二个&Name2被替换。

又比如:

string3 = %ScanRpl('&Name2' : 'Jack' : string2 :

结果为:

// string3 = 'Tom is Jack''s father, &Name2 is Tom''s son. ',只有第一个&Name2被替换,因为只搜索string2的第二个字符起29个字符,第二个&Name2将无法被搜索到。


l        内置函数参数应用:%LEN及其新特性(varying : *MAX)

众所周知,内置函数%LEN可以放在运算表达式的右侧来获取一个变量的长度,也可以放在运算表达式的左侧来设置一个变量的长度。如下面的例子:

D city                S   40A               varying inz('North York')

D n1                  S    5i 0

* 使用%LEN来获得变量长度:

/FREE

n1 = %len(city);

// 当前长度, n1 = 10

// 使用%LEN来设置变量长度

%len (city) = 5;

// city = 'North' 长度为5

/END-FREE

然而,从上面的例子我们可以看到,虽然我们定义了一个长度40(40A)的变量,但是我们真正能读取到的数据长度可以通过%LEN来设置。这样的应用在某种程度上屏蔽了我们对变量的长度定义,使得该变量“看起来”长度不固定。再来看下面的例子:

D char_varying      s   100a             varying

/FREE

char_varying = 'abc'; // 长度为3

len = %len(char_varying);

size = %size(char_varying);

// len = 3

// size = 102 (100 + 2)

/END-FREE

会不会很奇怪?这里一个看起来是3的字符,用%SIZE看到的却是102,即使我们能理解102长中的2是内嵌的数据区长度存放区。然而3和100这样完全没什么关联的长度也很容易让人困惑。

我们再来看下面的这个例子:

D char_varying     s   100a           varying

/FREE

char_varying = 'abc';

// 调用函数并传入变量地址变量长度

Conv (%ADDR(char_varying:*DATA) : %LEN(char_varying))

/END-FREE

这是我们在编程过程中可能经常使用的一种模式,传入变量地址和长度,然后针对该数据进行一些操作。发现问题了么?

对,这个长度可不是该数据的真实长度,%LEN的返回值是基于你针对该变量最后的一个%LEN操作,所以很明显你肯定得不到你想要的结果,而且甚至有可能产生脏数据。更严重的有可能导致整个应用系统的错误。由于这样的使用没有任何的语法错误,所以想发现它还需要花很多时间。

那么在一个大型应用中如何得到某个变量的确切长度呢?

很多的工程师使用%SIZE来巧妙的获取变量确切长度。大家知道,V6R1以前一个变量的数据区包括数据长度区(固定2个字节)和真实数据区,所以%LEN和%SIZE的关系也相对简单,即变量真实长度 = %SIZE - 2,如果是是UCS-2或者DBCS的数据,则变量真实长度 =  (%SIZE -2 )/2。然而从V6R1开始引入了变长数据的概念即数据长度区的长度和存放的数据相关,针对单字节变量是2而针对UCS-2和DBCS的变量是4。因为计算变量真实长度需要减去的数据长度区有可能是2也有可能是4,这就使得该方法不再完全适用了。

所以,为了获得变长变量的真实长度,%LEN在V7R1提供一个新的调用参数%LEN(varying:*MAX),使用该参数调用可以帮助你获得变长变量的最大可能长度也就是该变量的真实长度。例如,如果你定义一个UCS-2字符串变量FLD为25C,那么%LEN(FLD:*MAX)返回的就是25。

下面是一个更详细的%LEN(varying:*MAX)应用的例子:

 

D char_varying      s  100a              varying

D char_fld10        s   10a

/FREE

char_varying = 'abc'; // 长度为3

max_len = %len(char_varying : *MAX);

len = %len(char_varying);

size = %size(char_varying);

// max_len = 100,  len = 3,  size = 102 (100 + 2)

// 结合%TRIM的使用

char_fld10 = '1234';

max_len = %len(%trim(char_fld10) : *MAX);

len = %len(%trim(char_fld10));

// max_len = 10 , len = 4

// 调用函数并传入变量地址变量长度

Conv (%ADDR(char_varying:*DATA) : %LEN(char_varying:*MAX))

/END-FREE

从上面的例子我们可以看到,有了%LEN(varying:*MAX),我们就可以直接的得到一个变量的真实长度,从而为我们的变量地址和长度调用提供了有力的支持,避免了很多不必要的麻烦和错误。对我们实际的应用开发提供了很多便利。

 

[{"Business Unit":{"code":"BU058","label":"IBM Infrastructure w\/TPS"},"Product":{"code":"SWG60","label":"IBM i"},"Component":"","Platform":[{"code":"PF025","label":"Platform Independent"}],"Version":"","Edition":"","Line of Business":{"code":"LOB57","label":"Power"}}]

UID

ibm11146256