Read a packed decimal (Comp-3) number value which is 8 characters; but holds a 13 digit number and convert to 13 digits numeric value

0

I am seeking code to read a text file which is in packed decimal (Comp -3) numeric value which was created in main frames system and is 8 characters, but holds a 13 digit number in packed decimal format.

I came across below code

private Decimal Unpack(byte[] inp, int scale)
{
    long lo = 0;
    long mid = 0;
    long hi = 0;
    bool isNegative;

    // this nybble stores only the sign, not a digit.  
    // "C" hex is positive, "D" hex is negative, and "F" hex is unsigned. 
    switch (nibble(inp, 0))
    {
        case 0x0D:
            isNegative = true;
            break;
        case 0x0F:
        case 0x0C:
            isNegative = false;
            break;
        default:
            throw new Exception("Bad sign nibble");
    }
    long intermediate;
    long carry;
    long digit;
    for (int j = inp.Length * 2 - 1; j > 0; j--)
    {
        // multiply by 10
        intermediate = lo * 10;
        lo = intermediate & 0xffffffff;
        carry = intermediate >> 32;
        intermediate = mid * 10 + carry;
        mid = intermediate & 0xffffffff;
        carry = intermediate >> 32;
        intermediate = hi * 10 + carry;
        hi = intermediate & 0xffffffff;
        carry = intermediate >> 32;
        // By limiting input length to 14, we ensure overflow will never occur

        digit = nibble(inp, j);
        if (digit > 9)
        {
            throw new Exception("Bad digit");
        }
        intermediate = lo + digit;
        lo = intermediate & 0xffffffff;
        carry = intermediate >> 32;
        if (carry > 0)
        {
            intermediate = mid + carry;
            mid = intermediate & 0xffffffff;
            carry = intermediate >> 32;
            if (carry > 0)
            {
                intermediate = hi + carry;
                hi = intermediate & 0xffffffff;
                carry = intermediate >> 32;
                // carry should never be non-zero. Back up with validation
            }
        }
    }
    return new Decimal((int)lo, (int)mid, (int)hi, isNegative, (byte)scale);
}

private int nibble(byte[] inp, int nibbleNo)
{
    int b = inp[inp.Length - 1 - nibbleNo / 2];
    return (nibbleNo % 2 == 0) ? (b & 0x0000000F) : (b >> 4);
}

but above code fails saying bad sign nibble.

can anyone confirm if i am reading properly

 using (FileStream fs = new FileStream(pathSource, FileMode.Open))
            {
                using (StreamReader reader = new StreamReader(fs))
                {
                    List<decimal> list = new List<decimal>();

                    while (!reader.EndOfStream)
                    {
                        byte[] b = ASCIIEncoding.ASCII.GetBytes(reader.ReadLine());
                        list.Add(Unpack(b, 0));
                    }

                    reader.Close();
                }
            }

Note: it's not duplicated as I am looking for code that can read a file and pass a parameter to Unpack method.

For reference I added how the data inside the file looks like:

image

c#
.net
comp-3
packed-decimal
asked on Stack Overflow Nov 6, 2019 by VR1256 • edited Nov 25, 2019 by halfer

1 Answer

1

The “ASCII transfer type” will transfer the files as regular text files. So files becoming corrupt when we transfer packed decimal or binary data files in ASCII transfer type. The “Binary transfer type” will transfer the data in binary mode which handles the files as binary data instead of text data. So please use Binary transfer type for your case.

Reference : https://www.codeproject.com/Tips/673240/EBCDIC-to-ASCII-Converter

Once your file is ready, here is the code to convert packed decimal to human readable decimal.

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

   namespace ConsoleApp2
   {
    class Program
    {
        static void Main(string[] args)
        {
            var path = @"C:\FileName.BIN.dat";
            var templates = new List<Template>
            {
                new Template{StartPos=1,CharLength=4,Type="AlphaNum"},
                new Template{StartPos=5,CharLength=1,Type="AlphaNum"},
                new Template{StartPos=6,CharLength=8,Type="AlphaNum"},
                new Template{StartPos=14,CharLength=1,Type="AlphaNum"},
                new Template{StartPos=46,CharLength=4,Type="Packed",DecimalPlace=2},
                new Template{StartPos=54,CharLength=5,Type="Packed",DecimalPlace=0},
                new Template{StartPos=60,CharLength=4,Type="Packed",DecimalPlace=2},
                new Template{StartPos=64,CharLength=1,Type="AlphaNum"}
            };

            var allBytes = File.ReadAllBytes(path);
            for (int i = 0; i < allBytes.Length; i += 66)
            {
                var IsLastline = (allBytes.Length - i) < 66;
                var lineLength = IsLastline ? 64 : 66;
                byte[] lineBytes = new byte[lineLength];
                Array.Copy(allBytes, i, lineBytes, 0, lineLength);


                var outArray = new string[templates.Count];
                int index = 0;
                foreach (var temp in templates)
                {
                    byte[] amoutBytes = new byte[temp.CharLength];
                    Array.Copy(lineBytes, temp.StartPos - 1, amoutBytes, 0, 
    temp.CharLength);
                    var final = "";
                    if (temp.Type == "Packed")
                    {
                        final = Unpack(amoutBytes, temp.DecimalPlace).ToString();
                    }
                    else
                    {
                        final = ConvertEbcdicString(amoutBytes);
                    }

                    outArray[index] = final;
                    index++;

                }

                Console.WriteLine(string.Join(" ", outArray));

            }

            Console.ReadLine();
        }


        private static string ConvertEbcdicString(byte[] ebcdicBytes)
        {
            if (ebcdicBytes.All(p => p == 0x00 || p == 0xFF))
            {
                //Every byte is either 0x00 or 0xFF (fillers)
                return string.Empty;
            }

            Encoding ebcdicEnc = Encoding.GetEncoding("IBM037");
            string result = ebcdicEnc.GetString(ebcdicBytes); // convert EBCDIC Bytes -> 
    Unicode string
            return result;
        }

        private static Decimal Unpack(byte[] inp, int scale)
        {
            long lo = 0;
            long mid = 0;
            long hi = 0;
            bool isNegative;

            // this nybble stores only the sign, not a digit.  
            // "C" hex is positive, "D" hex is negative, AlphaNumd "F" hex is unsigned. 
            var ff = nibble(inp, 0);
            switch (ff)
            {
                case 0x0D:
                    isNegative = true;
                    break;
                case 0x0F:
                case 0x0C:
                    isNegative = false;
                    break;
                default:
                    throw new Exception("Bad sign nibble");
            }
            long intermediate;
            long carry;
            long digit;
            for (int j = inp.Length * 2 - 1; j > 0; j--)
            {
                // multiply by 10
                intermediate = lo * 10;
                lo = intermediate & 0xffffffff;
                carry = intermediate >> 32;
                intermediate = mid * 10 + carry;
                mid = intermediate & 0xffffffff;
                carry = intermediate >> 32;
                intermediate = hi * 10 + carry;
                hi = intermediate & 0xffffffff;
                carry = intermediate >> 32;
                // By limiting input length to 14, we ensure overflow will never occur

                digit = nibble(inp, j);
                if (digit > 9)
                {
                    throw new Exception("Bad digit");
                }
                intermediate = lo + digit;
                lo = intermediate & 0xffffffff;
                carry = intermediate >> 32;
                if (carry > 0)
                {
                    intermediate = mid + carry;
                    mid = intermediate & 0xffffffff;
                    carry = intermediate >> 32;
                    if (carry > 0)
                    {
                        intermediate = hi + carry;
                        hi = intermediate & 0xffffffff;
                        carry = intermediate >> 32;
                        // carry should never be non-zero. Back up with validation
                    }
                }
            }
            return new Decimal((int)lo, (int)mid, (int)hi, isNegative, (byte)scale);
        }

        private static int nibble(byte[] inp, int nibbleNo)
        {
            int b = inp[inp.Length - 1 - nibbleNo / 2];
            return (nibbleNo % 2 == 0) ? (b & 0x0000000F) : (b >> 4);
        }

        class Template
        {
            public string Name { get; set; }
            public string Type { get; set; }
            public int StartPos { get; set; }
            public int CharLength { get; set; }
            public int DecimalPlace { get; set; }
        }
    }
   }
answered on Stack Overflow Jun 5, 2020 by Sathyaraj Palanisamy • edited Jun 5, 2020 by Sathyaraj Palanisamy

User contributions licensed under CC BY-SA 3.0