Abdul Ahad
Abdul Ahad

Reputation: 188

How to parse data to and from cobol s9(6)v99 format in java

I am sending and recieving a products information from a server as a String, the server receive and send the price of the product in COBOLS s9(6)v99 format.I am unable to convert the given decimal from or to this format in java.

COBOL s9(6)V99 format exmaples:

Note: Currently I have no implementation of the conversion,I am looking for a solution

Upvotes: 1

Views: 6073

Answers (4)

dg1974
dg1974

Reputation: 3

In case its for IBM COBOL, the JZOS Record Generator with SYSADATA output from the z/OS COBOL compiler can be used to generate a Java Class representing COBOL structures and the getter and setter methods doing the conversions. Its binary (bitwise) compatible to the datatypes/structures in COBOL. It can be downloaded for free but requires an IBM ID. For compile outside z/OS the ibmjzos.jar is required, which is part of the JDK for z/OS.

Upvotes: 0

user20885329
user20885329

Reputation: 1

It would be much easier to have cobol in the server move the values into a screen variable like

77 S-VALUE PIC  ------9,99

and export this variable which is easily readable and can be parsed into a Java double variable with:

Double value = Double.parseDouble(string);

or

Double value = new Double(string);

Upvotes: 0

Bruce Martin
Bruce Martin

Reputation: 10553

Suggestions

In general

  • Change the Cobol. If the cobol was changed to s9(6)V99 sign leading it would be a lot easier to handle in java. In your case, this is probably not an option
  • If you can get the Cobol Copybook use a package

Packages

If you can get the Cobol Copybook, why not use one of the Cobol / Java packages

Note: Even if you do not have the Full Cobol Copybook, you could set up a Cobol Copybook for this one field and still use a package. The copybook would be:

   01  MY-REC.
       03 FIELD-1              PIC S9(6)V99.

What you need to know

There is no one single Cobol Zoned Decimal format, it varies from compilere to Compiler and what the encoding is. To decode Zoned Decimal you really need to know the

  • The Cobol Compiler
  • The Encoding used on the Server

In this case I would guess it is

  • IBM Compiler running on IBM Mainframe or AS400
  • US Ebdic (IBM037) or something similar (definitely not German EBCDIC (IBM273))

Interpretting Zoned decimal

In Zoned decimal:

  • S indicates it is a signed number; the sign will be overpunched on the last numeric digit.
  • 9 represents a single digit
  • V represents an assumed decimal place

So s9(6)V99 is a signed number with 6 digits before the decimal place + 2 after

Effect of Encoding

The encoding (characterset) used by the server determines how the sign digit is represented. For US (and UK) Ebcdic +0/-0 are { / } but they are different for German Ebcdic. For ASCII servers it is different again

Java Code

The ebcdic conversion code (note it still needs to be adjusted for the assumed decimal):

private static int positiveDiff = 'A' - '1';
private static int negativeDiff = 'J' - '1';

private static char positive0EbcdicZoned = '{';
private static char negative0EbcdicZoned = '}';

public static String fromZoned(String numZoned) {
    String ret;
    String sign = "";
    char lastChar, ucLastChar;

    if (numZoned == null || ((ret = numZoned.trim()).length() == 0) || ret.equals("-")) {
        return "";
    }

    lastChar = ret.charAt(ret.length() - 1);
    ucLastChar = Character.toUpperCase(lastChar);


    switch (ucLastChar) {
    case 'A':
    case 'B':
    case 'C':
    case 'D':
    case 'E':
    case 'F':
    case 'G':
    case 'H':
    case 'I':
        lastChar = (char) (ucLastChar - positiveDiff);
        break;
    case 'J':
    case 'K':
    case 'L':
    case 'M':
    case 'N':
    case 'O':
    case 'P':
    case 'Q':
    case 'R':
        sign = "-";
        lastChar = (char) (ucLastChar - negativeDiff);
        break;
    default:
        if (lastChar == positive0EbcdicZoned) {
            lastChar = '0';
        } else if (lastChar == negative0EbcdicZoned) {
            lastChar = '0';
            sign = "-";
        }           
    }
    ret = sign + ret.substring(0, ret.length() - 1) + lastChar;

    return ret;
}

To set +0/-0 characters

public static void setDefaultEbcidicCharacterset(String charset) {
    if (getHold(charset).isEbcdic) {
        byte[] b = {(byte) 0xC0, (byte) 0xD0};
        String s = new String(b, charset);
        if (s.length() == 2) {
            positive0EbcdicZoned = s.charAt(0);
            negative0EbcdicZoned = s.charAt(1);
        }
    }
}

An alternative way to derive the sign (for EBCDIC encoding) is to convert the sign back into the raw bytes:

private static final byte HIGH_NYBLE = (byte) 0xf0;
private static final byte ZONED_NEGATIVE_NYBLE_VALUE = (byte) 0xD0;

    String Sign = "";
    byte signByte = signStr.getBytes(encoding)[0];
    if (((byte) (signByte & HIGH_NYBLE)) == ZONED_NEGATIVE_NYBLE_VALUE) {
        sign = "-";
    }
    byte lastDigitBytes = (byte) (signByte | HIGH_NYBLE);

ASCII Cobols

In this case it is EBCDIC. For ASCII based cobols it is different again. This is JRecord generic conversion class for Ascii Zoned-Decimal:

Note: I am the author JRecord

Upvotes: 8

theblitz
theblitz

Reputation: 6881

From what I can see it should be reasonably straightforward. This assumes, which seems to be the case from your example, that this is a Zoned Decimal.

First you will need to get the sign of the number. Simply check the last character. If it is a non-digit then it is negative (assuming you are using F format for the positive number). Once you have that you can then replace that character with the correct, equivalent digit.

You now have a string representation of a number.

Now do

Integer result = Integer.valueOf(theInputString)

then divide by 100 and re-apply the sign. You could also just add the sign as a "-" or "+" to the string before calling valueOf.

Upvotes: 0

Related Questions