-->

Unable to unpack comp 3 to numeric in java

2019-03-05 04:38发布

问题:

I referred a code on the internet to unpack comp 3 to numeric in java. I tried to pass a sample comp3 file to the code but I didn't get the proper unpacked data. I got some weird numbers. I am new to this concept(comp 3) so can you guys help me on this. Thanks in advance

Below is my code


import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * Converts between integer and an array of bytes in IBM mainframe packed
 * decimal format. The number of bytes required to store an integer is (digits +
 * 1) / 2. For example, a 7 digit number can be stored in 4 bytes. Each pair of
 * digits is packed into the two nibbles of one byte. The last nibble contains
 * the sign, 0F for positive and 0C for negative. For example 7654321 becomes
 * 0x76 0x54 0x32 0x1F.
 * 
 * This class is immutable. Once constructed you can extract the value as an
 * int, an array of bytes but you cannot change the value. Someone should
 * implement equals() and hashcode() to make this thing truly useful.
 */

public class PackedDecimalToComp {

    public static void main(String[] args) {

        try {
            // test.unpackData(" 0x12345s");
            Path path = Paths.get("C:\\Users\\AV00499269\\Desktop\\Comp3 data file\\Comp3Test.txt");
            byte[] data = Files.readAllBytes(path);
            PackedDecimalToComp test = new PackedDecimalToComp();
            test.unpackData(data);
        } catch (Exception ex) {
            System.out.println("Exception is :" + ex.getMessage());
        }    
    }

    private static String unpackData(byte[] packedData) {
        String unpackedData = "";

        final int negativeSign = 13;
        for (int currentCharIndex = 0; currentCharIndex < packedData.length; currentCharIndex++) {
            byte firstDigit = (byte) ((packedData[currentCharIndex] >>> 4) & 0x0F);
            byte secondDigit = (byte) (packedData[currentCharIndex] & 0x0F);
            unpackedData += String.valueOf(firstDigit);
            if (currentCharIndex == (packedData.length - 1)) {
                if (secondDigit == negativeSign) {
                    unpackedData = "-" + unpackedData;
                }
            } else {
                unpackedData += String.valueOf(secondDigit);
            }
        }
        System.out.println("Unpackeddata is :" + unpackedData);

        return unpackedData;
    }    
}

Comp3 file I passed has values x019F

When converted I got the unpacked data as 783031394

回答1:

You can use the IBM Record Generator for Java, a free tool.

This allows you to generate a Java class that represents a COBOL or PL/I DSECT which you can then use in your own code to read/write values to most COBOL and PL/I data types. If you aren't working with a structure then you can see through the code how the underlying JZOS classes are used to interact with the datatype.

Although the tool is free it is supported by IBM, so if you hit an issue you can raise a problem with IBM and they will fix it.



回答2:

Handling Mainframe binary file

You have 2 options:

  • Convert the file to a Text file on the mainframe and then transfer the file
  • Do a binary transfer and keep the file as EBCDIC then use something like JRecord to read the file. You can also use RecordEditor to edit the file

I have also seen files converted to Unix EBCDIC Text files on the mainframe and a binary Transfer (keep as EBCDIC). This is done for non-English EBCDIC where you have special language specific characters. Java editors (e.g. JEdit) have no trouble editing Unix Ebcdic files

File Transfer (Mainframe)

To transfer a Binary file (has comp, comp-3 etc) from the Mainframe to Windows / *nix box you must do a Binary Transfer for a very simple reason: the Ebcdic --> Ascii program can not distinguish between binary fields and Text fields.

Comp-3 value   hex     hex after Ascii conversion

 400          x'400c'       x'200c'       x'40' is the ebcdic space character
                                          it gets converted to the ascii
                                          space character x'20'

You need to do a Binary transfer from the Mainframe. This will keep the file as EBCDIC and any binary fields will be untouched. You then read the file using Ebcdic.

You will need to check the RECFM on the Mainframe. If the RECFM is

  • FB - no problems just transfer
  • VB - either convert to FB on the mainframe of include the RDW (Record Descriptor Word) option in the file transfer.
  • Other - Convert to FB/VB on the mainframe

RecordEditor

  • Make sure you have Java installed
  • You can get the RecordEditor from here. You can either download an installer or the USB version. The USB version is a zipped Directory which you can install in any normal directory.

Using The Record Editor

You need to define the Record Layout (or file schema). The easiest way is to import a Cobol Copybook.

For a 2 byte comp-3 field, create a cobol copybook with

        01  Tst-Record.
            03  comp3-field              pic s9(3) comp-3.

To import the cobol copybook Select Record Layouts >>> Load Cobol Copybook

Then enter the Cobol copybook file name and press the load Cobol button at the bottom of the screen

Next go the file open screen and enter the file name and select the copybook you have just imported:

Hit enter and you should be able update the file:

Generating Code

The RecordEditor can also generate code for the JRecord Library. See How do you generate java~jrecord code for a Cobol copybook for hot to generate Java~JRecord Code using a Cobol Copybook.

You can also generate Code that does not use a Cobol Copybook wile editting the file:

Select Generate >>> Generate Code from the File option. Thene select a template and enter a package Id