Find Decimal Data Errors

Article ID: 56974

I often get questions about finding and fixing bad data in files. Predominantly, the bad data leads to the dreaded "decimal data error," and folks want to know how to write programs that can identify which fields of which records in a file have bad decimal data. This article presents a programming technique that helps you locate decimal data errors in a file.

Many ways to code something like this exist, but my favorite technique for detecting decimal data errors is by reading data directly into an RPG data structure. For example, consider the following code:

     FITMMAST   IF   E           K DISK    BLOCK(*YES)
     F                                     INFDS(dsInfo)
     FITEMOUT   IF   E           K DISK    RENAME(ITMMASTF:ITEMOUTF)

     D dsInfo          ds
     D   RRN                 397    400i 0

     D InRec           ds                  likerec(ITMMASTF:*INPUT)
     D OutRec          ds                  likerec(ITEMOUTF:*INPUT)
     D errMsg          s             80a   varying

      /free

          setll *start ITMMAST;
          read ITMMASTF InRec;

          dow not %eof(ITMMAST);

           ... do something with the record ...

             read ITMMASTF InRec;
          enddo;

The preceding code reads through the entire ITMMAST file. (ITMMASTF is the name of the record format of the ITMMAST file.) The interesting part is that it reads each record directly into a data structure named InRec. The code accomplishes this by specifying InRec in the result field of the READ opcode.

Reading an externally defined file directly into a data structure first became possible in V5R2. In that release, the READ, CHAIN, READE, READP, and READPE opcodes can read directly into a data structure if the structure is defined with LIKEREC, but only if the record format name is used instead of the file name.

In V5R3, some of these restrictions were relaxed a bit. You can now use EXTNAME data structures as well as LIKEREC data structures, and you can use the file name instead of the record format name, provided that your file has only one record format.

Anyway, the fact that the READ opcode reads directly into a data structure is useful because the system copies the entire record, as a block of bytes, directly from the file into my data structure. This behavior is different from traditional file access; RPG's traditional access would read one field at a time. This new behavior is useful for finding decimal data errors, because the system doesn't even look at the value in the decimal fields--it simply copies them byte for byte from the file to your program. Now that you have the records in your program, you can test them to see if there's any problem. For example:

     FITMMAST   IF   E           K DISK    BLOCK(*YES)
     F                                     INFDS(dsInfo)
     FITEMOUT   IF   E           K DISK    RENAME(ITMMASTF:ITEMOUTF)

     D dsInfo          ds
     D   RRN                 397    400i 0

     D InRec           ds                  likerec(ITMMASTF:*INPUT)
     D OutRec          ds                  likerec(ITEMOUTF:*INPUT)
     D errMsg          s             80a   varying

      /free

          setll *start ITMMAST;
          read ITMMASTF InRec;

          dow not %eof(ITMMAST);

             monitor;
                OutRec        = InRec;
                OutRec.Price  = InRec.Price;
                OutRec.Weight = InRec.Weight;
                OutRec.Height = InRec.Height;
                OutRec.Length = InRec.Length;
                OutRec.Width  = InRec.Width;
                OutRec.Depth  = InRec.Depth;
                // (or use eval-corr in V5R4)
                write ITEMOUTF OutRec;
             on-error 907;
                errMsg = 'Decimal data error on record ' + %char(RRN);
                // show message to user
             endmon;

             read ITMMASTF InRec;
          enddo;

          *inlr = *on;

      /end-free

This sample program reads each record from the ITMMAST file. The records that have no errors are copied to the ITEMOUT file. The records that do have errors are not copied; instead, a message is prepared to tell the user which RRN of the file had bad data. (The sample code creates this message in the errMsg variable. I've left it as an exercise for the reader to figure out how to display the message to the user.)

How does the program detect the error? It detects the error while copying the data from the ITMMAST file record to the ITEMOUT file record. The program starts by copying the whole record by doing OutRec = InRec. Then, it copies each numeric field (individually) from InRec to OutRec. If one of those fields contains invalid decimal data, the system produces an RPG status 907 error as the data is read from the InRec structure. When that happens, my program catches the error (by using the MONITOR statement to trap status 907) and then reports the problem to the user.

If you prefer to know which field has bad data, you might code it a little differently, like this:

             OutRec = InRec;

             monitor;
                OutRec.Price  = InRec.Price;
             on-error 907;
                errMsg = 'Error in PRICE field on record ' + %char(RRN);
                // show message to user
             endmon;

             monitor;
                OutRec.Weight  = InRec.Weight;
             on-error 907;
                errMsg = 'Error in WEIGHT field on record ' + %char(RRN);
                // show message to user
             endmon;

    . . . and so forth, for all the numeric fields . . .

Good luck!

ProVIP Sponsors

ProVIP Sponsors