1

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:

  • 0000016H
  • 0000000{

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

Abdul Ahad
  • 188
  • 2
  • 14
  • 1
    So would you mind giving some example of the data to parse? I guess 99,9% of users here have never used COBOL! – Gyro Gearless Mar 06 '17 at 14:25
  • See http://stackoverflow.com/questions/3257771/how-to-format-a-number-to-s95v99-ascii-in-net – OldCurmudgeon Mar 06 '17 at 14:30
  • i have read that discussion and it concludes to nowhere... – Abdul Ahad Mar 06 '17 at 14:35
  • In what format are you receiving the data? string, binary, integers? – Tschallacka Mar 06 '17 at 14:35
  • I am sending and receiving data in string format – Abdul Ahad Mar 06 '17 at 14:37
  • Edit that into your question, also add a code sample what you currently have where you parse the cobol string to java object, and java object to cobol string. – Tschallacka Mar 06 '17 at 14:39
  • At first sight I think you have an encoding problem, not a parsing problem. `S9(6)V99` means that you should expect to receive a signed 8 digit field and that there is an implied decimal point after the first 6 digits. So, if you receive +12345678, that should be interpreted as +123456.78. The fact you are receiving data such as `0000016H` means that either your data layout in your java code does not match the Cobol data layout, or that the data has been encoded with a different character set. – DaveH Mar 06 '17 at 14:54
  • It looks like some screwed-up version of a Packed Decimal (Comp-3). The S at the beginning means it is a signed number. If it is PD then the last character should be C (+) or D(-). – theblitz Mar 06 '17 at 15:06
  • At a second glance it does actually look like Zoned Decimal. Thus the first number is +168 and the second one is -0 (?). – theblitz Mar 06 '17 at 15:18
  • 1
    So, get the sender to send it in a format where you don't have to do any coding. Chose what is convenient for you (in a fixed-width field) and it is a very minor change to the COBOL program. – Bill Woodger Mar 06 '17 at 15:50
  • 2
    @theblitz that's not a negative zero. – Bill Woodger Mar 06 '17 at 15:55
  • And where is your data coming from? A Mainframe? – Bill Woodger Mar 06 '17 at 15:55
  • @BillWoodger Yeah. My bad. My knowledge of the EBCDIC character set is kinda rusty. Thought { was D0 not C0. – theblitz Mar 06 '17 at 18:18
  • @BillWoodger i can not force the server to change the format, actually it is a insurance clearing house(switch) with which i am communicating. – Abdul Ahad Mar 06 '17 at 18:36
  • @DaveH i have checked the guide provided by the server, the data is encoded in **s9(6)v99**, and it is not corrupted. – Abdul Ahad Mar 06 '17 at 18:37

4 Answers4

8

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

Bruce Martin
  • 10,358
  • 1
  • 27
  • 38
0

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.

theblitz
  • 6,683
  • 16
  • 60
  • 114
  • 2
    "using F format for the positive number" is less common than you might think. – cschneid Mar 06 '17 at 15:50
  • Since I was typing at the same time, very much less common than you think. Certainly for most COBOL systems it would be a potentially very, very, bad thing. It also indicates shoddiness. – Bill Woodger Mar 06 '17 at 15:52
  • Long time since I programmed in Cobol. Nevertheless, the idea still stands. – theblitz Mar 06 '17 at 18:13
  • @theblitz can you please describe that what "H" and "{" means here – Abdul Ahad Mar 06 '17 at 18:42
  • A couple of links that will explain it better than I can: http://faculty.cs.niu.edu/~byrnes/csci360/notes/360dec.htm https://v8doc.sas.com/sashtml/lrcon/z1265705.htm – theblitz Mar 06 '17 at 19:10
0

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);
ahuemmer
  • 1,653
  • 9
  • 22
  • 29
0

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.

dg1974
  • 3
  • 1