Wednesday, May 28, 2025

Increment a number held in a character field

The title is simplistic for what this post is really about. The question that was asked was:

I have a character field in a DDS physical file that is 20 characters long. There can be various numbers in that field of any length. I want to retrieve the value from the field, increment it by one, and update the field in the file.

If all the "numbers" in the field in the file were 20 characters long it would be easy, but as the "numbers" can be of different lengths it makes the problem more interesting.

First I need a file for this 20 character field. I decided to call it TESTFILE and the DDS code for it is:

01 A                                      UNIQUE
02 A          R TESTFILER
03 A            KEY            5A
04 A            FIELD1        20A
05 A          K KEY

Line 1: If a file is keyed, I always like that key to be unique, to prevent duplicate keyed records.

Line 2: Record format name is TESTFILER.

Line 3: The first field is the key field.

Line 4: This is the 20 character field that will contain the "numbers".

Line 5: This is the line that gives the key field for the file.

The file contains the following records:

KEY    FIELD1
-----  --------------------
KEY 1  00000
KEY 2  00000000000000000000
KEY 3  0
  • "KEY 1" is a five long "number".
  • "KEY 2" the "number" is 20 long.
  • "KEY 3" the "number" just one long.

Rather than read all the records sequentially I am going to randomly retrieve them, using the CHAIN operation code, and then increment each one.

I will show my RPG program in two parts: First the main body, and the second is the subprocedure that does the incrementing.

Here is the main body:

01  **free
02  ctl-opt dftactgrp(*no) ;

03  dcl-f TESTFILE usage(*update) keyed ;

04  chain 'KEY 2' TESTFILER ;
05  FIELD1 = Increment(FIELD1) ;
06  update TESTFILER %fields(FIELD1) ;

07  chain 'KEY 1' TESTFILER ;
08  FIELD1 = Increment(FIELD1) ;
09  update TESTFILER %fields(FIELD1) ;

10  chain 'KEY 3' TESTFILER ;
11  FIELD1 = Increment(FIELD1) ;
12  update TESTFILER %fields(FIELD1) ;

13  *inlr = *on ;

Line 2: As the program contains a subprocedure it cannot be executed in default activation group, therefore, I need this control option.

Line 3: My DDS file is defined for update and keyed. If the file was not keyed, I could not use the CHAIN operation code to get the records I desire.

Line 4: I CHAIN the file with "KEY 2", which has the 20 long "number".

Line 5: Call the subprocedure Increment passing to it the contents of FIELD1 from the file. And has the changed value returned from the subprocedure into FIELD1.

Line 6: I update the record in the file. Thanks to the %FIELDS Built in Function, BiF, only FIELD1 is updated. Even if the field KEY has been changed the file is not updated with the changed value.

Lines 7 – 9: The same but with the record with the key "KEY 1", which is the five long number.

Lines 10 – 12: One more time for the record with the key "KEY 3", the one long number.

What does the subprocedure do with the pass 20 character string.

14  dcl-proc Increment ;
15    dcl-pi *n char(20) ;
16      inValue char(20) ;
17    end-pi ;

18    dcl-s Length uns(3) ;

19    Length = %len(%trimr(inValue)) ;

20    evalr inValue = %editc(%dec(inValue : 20 : 0) + 1 : 'X') ;
21    inValue = %subst(inValue : (20 - Length) + 1) ;

22    return inValue ;
23  end-proc ;

Lines 15 – 17: The procedure interface. Coming into the subprocedure a 20 character variable called inValue. And a 20 character value is returned.

Line 18: I need this variable to contain the length of the "number" within the 20 character variable.

Line 19: Here I get the length of the "number". I trim right inValue, and then use the %LEN BiF to retrieve the length of the "number".

Line 20: This looks complicated, but it is not. Let me break apart the statement into its constituent parts:

  1. %DEC:  Converts the character value in inValue to a 20 decimal value.
  2. + 1:  Increment the decimal value.
  3. %EDITC:  Convert the decimal value back to character. I use the edit code 'X' as it does not add a comma separator, etc.
  4. EVALR:  The result of the %EDITC is actually 21 characters. I need to use the EVALR:  Operation code to right adjust the result from the %EDITC.

Line 21: This is where I extract the appropriate length of the "number" using a %SUBST BiF.

Line 22: The value in inValue is returned.

After creating the program, when I call it the records in the file have been incremented:

KEY    FIELD1
-----  --------------------
KEY 1  00001
KEY 2  00000000000000000001
KEY 3  1

For something that sounded complicated, it was actually simple to do.

 

This article was written for IBM i 7.6, and should work for some earlier releases too.

4 comments:

  1. 21 inValue = %right(inValue : Length) ;

    ReplyDelete
  2. You should also include the control option
    EXPROPTS(*ALWBLANKNUM)
    for the case that FIELD1 is blank

    ReplyDelete
  3. Hi, just for fun - with SQL:

    with t1(the_string)
    as( values('ABC123DEF'),
    ('1xyz '),
    ('EmbedBl 8'),
    ('CATCH22 '))
    select
    regexp_replace(the_string,
    regexp_substr( the_string,'\d+'),
    regexp_substr( the_string,'\d+')+1 )
    from t1

    ReplyDelete

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.