JinC
JinC

Reputation: 33

How can I convert a Cobol COMP field output to readable decimal in C#?

In converting a cobol program to C#, I encountered COMP:

03  Var1                     PIC X(4).
03  Var2                     PIC X(3).
03  Var3                     PIC X(3).
03  Var4                     PIC X(4).
03  Var5                     PIC X(16).
03  Var6                     PIC X(4).
03  Var7                     PIC X(2).
03  Var8                     PIC X.
03  Var9                     PIC X(4).
03  Var10                    PIC X(16).
03  Var11                    PIC S9(7)V9(2) COMP.
03  Var12                    PIC S9(7)V9(2) COMP.
03  Var13                    PIC S9(7)V9(2) COMP.
03  Var14                    PIC S9(7)V9(2) COMP.
03  Var15                    PIC S9(7)V9(2) COMP.
03  Var16                    PIC S9(7)V9(2) COMP.
03  Var17                    PIC S9(7)V9(2) COMP.
03  Var18                    PIC S9(7)V9(2) COMP.
03  Var19                    PIC S9(7)V9(2) COMP.
03  Var20                    PIC S9(7)V9(2) COMP.
03  Var21                    PIC S9(7)V9(2) COMP.
03  Var22                    PIC S9(7)V9(2) COMP.
03  Var23                    PIC S9(7)V9(2) COMP.
03  Var24                    PIC S9(7)V9(2) COMP.

I've spent several hours looking into COMP. Most searches yield something about COMP-3 or mention that COMP is a binary conversion. However, the cobol program's COMP output is the non-COMP fields followed by (between the parentheses):

( F ” " )

while the actual values are all 0.00, except that var13 is 64.70

NOTE: these are the values copied from Notepad++. Also, note that I know very little about cobol.

How can I convert from COMP to decimal? Ideally, I could also convert decimal to COMP as well, as I need to put things back into the same format.

I have tried reading the data in as binary with:

public static void ReadBinaryFile(string directoryString)
    {
        using (BinaryReader reader = new BinaryReader(File.Open(directoryString, FileMode.Open)))
        {
            string myString = Encoding.ASCII.GetString(reader.ReadBytes(113));
            Console.WriteLine(myString);
        }
    }

EDIT: On the right track

Thanks to @piet.t and @jdweng for the help.

While there is still an issue with this test code, this should help anyone in my position with their solution:

public static void ReadBinaryFile(string directoryString)
    {
        using (BinaryReader reader = new BinaryReader(File.Open(directoryString, FileMode.Open)))
        {
            string asciiPortion = Encoding.ASCII.GetString(reader.ReadBytes(57)); // Read the non-comp values

            Console.Write(asciiPortion); // Test the ascii portion 

            Console.WriteLine("var11: " + reader.ReadInt32());
            Console.WriteLine("var12: " + reader.ReadInt32());
            Console.WriteLine("var13: " + reader.ReadInt32());
            Console.WriteLine("var14: " + reader.ReadInt32());
            Console.WriteLine("var15: " + reader.ReadInt32());
            Console.WriteLine("var16: " + reader.ReadInt32());
            Console.WriteLine("var17: " + reader.ReadInt32());
            Console.WriteLine("var18: " + reader.ReadInt32());
            Console.WriteLine("var19: " + reader.ReadInt32());
            Console.WriteLine("var20: " + reader.ReadInt32());
            Console.WriteLine("var21: " + reader.ReadInt32());
            Console.WriteLine("var22: " + reader.ReadInt32());
            Console.WriteLine("var23: " + reader.ReadInt32());
            Console.WriteLine("var24: " + reader.ReadInt32());
        }
    }

EDIT 2: Trying to find the issue

Issue: every value appears to be followed by some garbage value which is printed as the next int32.

Actual values:

var11 = var12 = 0.00
var13 = 58.90
var14 = 0.00
var15 = -0.14
var16 = 0.00
var17 = var18 = var19 = var20 = 0.00
var21 = var22 = var23 = var24 = 0.00

Output (with padding):

Var11:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var12:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var13:  5890  HEX: 00001702  BIN: 00000000000000000001011100000010
Var14:   368  HEX: 00000170  BIN: 00000000000000000000000101110000
Var15:   -14  HEX: FFFFFFF2  BIN: 11111111111111111111111111110010
Var16:    -1  HEX: FFFFFFFF  BIN: 11111111111111111111111111111111
Var17:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var18:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var19:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var20:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var21:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var22:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var23:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var24:     0  HEX: 00000000  BIN: 00000000000000000000000000000000

Notepad++ (Copied) Representation:

          p  òÿÿÿÿÿÿÿ                                

Notepad++ (Visual) Representation:

[NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][STX][ETB][NUL][NUL]p[SOH]
[NUL][NUL]òÿÿÿÿÿÿÿ[NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL]
[NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL]
[NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][LF]

EDIT 3: Solution!

@piet.t had it all right. Thanks for a useful answer to my first question! The issue was something specific to the cobol program. I was led to believe Var14 was always 0, but:

Var14 = SomeCalculationIHadNoIdeaAbout(Var13, SomeOtherNumber);

I used RecordEdit to tweak the data more easily (Warning: the program is a little strange in places) and noticed an odd trend in the "garbage" values.

The real solution to my problem is the code in the first EDIT, which I made days ago :/.

NOTE: I also had to consume a line feed character, which I did not put in that code. To do that, just add another reader.ReadBytes(1);.

NOTE 2: You may need to look into EBDCDIC and/or Endianness, which may make your solution a bit more difficult than mine.

Upvotes: 3

Views: 4309

Answers (1)

piet.t
piet.t

Reputation: 11911

Things will get a little complicated here since the COBOL-Program is using fixed-point variables that I think C# doesn't know.

For conversion treat each PIC S9(7)V9(2) COMP-field as an Int32 (it should be BigEndian format). But note that you will not get the actual value but value*100 because of the implicit decimal point in the COBOL field-declaration.

Pay attention that using fixed-point data will allow exact calculations for values with decimals while converting it to floating point in C# may result in rounding since binary floating points can't always exactly represent decimals.

Upvotes: 7

Related Questions