I watched posts on numerous tips regarding converting Comp-3 BCD data from "old" mainframes to something that can be used in C #. Firstly, I would like to say that I am less than enthusiastic about the answers that were received by some of these posts, especially those that said in essence: "Why are you bothering us with these messages other than C # / C + + ", as well as" If you need an answer about some COBOL agreement, why don't you go to a COBOL-oriented site. " For me, this is a complete BS, because for software developers, unfortunately, it will probably take many years to figure out how to deal with some of these problems that exist in THE REAL WORLD. Thus, even if I slam this post for the following code, I am going to share with you the REAL WORLD experience that I had to deal with regarding COMP-3 / EBCDIC conversion (and yes, I am the one who talks about “floppy disks, paper tapes, discs, etc. - I have been a software engineer since 1979).
First you need to understand that any file that you read from an outdated system of the main frame, for example IBM, will present you data in EBCDIC format and to convert any of this data to a C # / C ++ string that you may allow you have to use correct translation of the code page to receive data in ASCII format. A good example of how to handle this would be:
StreamReader readFile = new StreamReader (path, encoding .GetEncoding (037); // 037 = EBCDIC to ASCII conversion.
This ensures that everything you read from this stream is then converted to ASCII and can be used in string format. This includes the “Zoned Decimal” (Fig. 9) and “Text” (Fig. X) fields as announced by COBOL. However, this does not necessarily convert the COMP-3 fields to the correct “binary” equivalent when reading into a char [] or byte [] array. To do this, the only way you are ever going to translate it correctly (even using the code pages UTF-8, UTF-16, Default or something else), you need to open the file as follows:
FileStream fileStream = new FileStream (path, FIleMode.Open, FIleAccess.Read, FileShare.Read);
Of course, the "FileShare.Read" option is "optional."
When you isolate the field that you want to convert to a decimal value (and then, if necessary, to an ASCII string), you can use the following code - and this was mainly stolen from MicroSoft's “UnpackDecimal” message, which you can get:
http://www.microsoft.com/downloads/details.aspx?familyid=0e4bba52-cc52-4d89-8590-cda297ff7fbd&displaylang=en
I highlighted (I think) what are the most important parts of this logic and combined it into two methods that you can do with what you want. For my purposes, I decided to leave this as a decimal value that I could do with what I wanted. Basically, the method is called “unzip”, and you pass it a byte [] array (no more than 12 bytes), and the scale as int, which is the number of decimal places that you want to return in decimal value. I hope this works for you, as it does for me.
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); }
If you have any questions, post them here because I suspect that I am going to get a “blazing” one, like everyone who decided to post questions related to today's issues ...
Thanks, John is an elder.