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:
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; }
}
}
}
User contributions licensed under CC BY-SA 3.0