using System; using System.Collections.Generic; using System.Globalization; using System.Text; namespace Unplugged.IbmBits { public static class IbmConverter { #region String static readonly Encoding _unicode = Encoding.Unicode; static readonly Encoding _ebcdic = Encoding.GetEncoding("IBM037"); /// /// Returns a Unicode string converted from a byte array of EBCDIC encoded characters /// public static string ToString(byte[] value) { return ToString(value, 0); } /// /// Returns a Unicode string converted from a byte array of EBCDIC encoded characters /// starting at the specified position /// /// /// Zero-based index of starting position in value array /// public static string ToString(byte[] value, int startingIndex) { if (ReferenceEquals(null, value)) throw new ArgumentNullException("value"); return ToString(value, startingIndex, value.Length - startingIndex); } /// /// Returns a Unicode string converted from a byte array of EBCDIC encoded characters /// starting at the specified position of the given length /// /// /// Zero-based index of starting position in value array /// /// /// Number of characters to convert /// public static string ToString(byte[] value, int startingIndex, int length) { var unicodeBytes = Encoding.Convert(_ebcdic, _unicode, value, startingIndex, length); return _unicode.GetString(unicodeBytes); } /// /// Returns a byte array of EBCDIC encoded characters converted from a Unicode string /// public static byte[] GetBytes(string value) { return GetBytes(value, 0); } /// /// Returns a byte array of EBCDIC encoded characters converted from a Unicode substring /// starting at the specified position /// /// /// Zero-based starting index of substring /// public static byte[] GetBytes(string value, int startingIndex) { return GetBytes(value, startingIndex, value.Length - startingIndex); } /// /// Returns a byte array of EBCDIC encoded characters converted from a Unicode substring /// starting at the specified position with the given length /// /// /// Zero-based starting index of substring /// /// /// Number of characters to convert /// public static byte[] GetBytes(string value, int startingIndex, int length) { if (ReferenceEquals(null, value)) throw new ArgumentNullException("value"); var unicodeBytes = _unicode.GetBytes(value.ToCharArray(startingIndex, length)); return Encoding.Convert(_unicode, _ebcdic, unicodeBytes); } #endregion #region Int16 /// /// Returns a 16-bit signed integer converted from two bytes encoding a big endian 16-bit signed integer /// public static Int16 ToInt16(byte[] value) { return ToInt16(value, 0); } /// /// Returns a 16-bit signed integer converted from two bytes in a specified position encoding a big endian 16-bit signed integer /// public static Int16 ToInt16(byte[] value, int startIndex) { if (ReferenceEquals(null, value)) throw new ArgumentNullException("value"); var bytes = new byte[] { value[startIndex + 1], value[startIndex] }; return BitConverter.ToInt16(bytes, 0); } /// /// Returns two bytes encoding a big endian 16-bit signed integer given a 16-bit signed integer /// public static byte[] GetBytes(short value) { var b = BitConverter.GetBytes(value); return new byte[] { b[1], b[0] }; } #endregion #region Int32 /// /// Returns a 32-bit signed integer converted from four bytes encoding a big endian 32-bit signed integer /// public static int ToInt32(byte[] value) { return ToInt32(value, 0); } /// /// Returns a 32-bit signed integer converted from four bytes at a specified position encoding a big endian 32-bit signed integer /// public static Int32 ToInt32(byte[] value, int startIndex) { if (ReferenceEquals(null, value)) throw new ArgumentNullException("value"); var bytes = new byte[] { value[startIndex + 3], value[startIndex + 2], value[startIndex + 1], value[startIndex] }; return BitConverter.ToInt32(bytes, 0); } public static byte[] GetBytes(int value) { var b = BitConverter.GetBytes(value); return new byte[] { b[3], b[2], b[1], b[0] }; } #endregion #region Single static readonly int _ibmBase = 16; static readonly byte _exponentBias = 64; static readonly int _threeByteShift = 16777216; /// /// Returns a 32-bit IEEE single precision floating point number from four bytes encoding /// a single precision number in IBM System/360 Floating Point format /// public static Single ToSingle(byte[] value) { if (ReferenceEquals(null, value)) throw new ArgumentNullException("value"); if (0 == BitConverter.ToInt32(value, 0)) return 0; // The first bit is the sign. The next 7 bits are the exponent. byte exponentBits = value[0]; var sign = +1.0; // Remove sign from first bit if (exponentBits >= 128) { sign = -1.0; exponentBits -= 128; } // Remove the bias from the exponent // exponentBits -= _exponentBias); // 出现负数时,出错 var exponent = Math.Pow(_ibmBase, exponentBits - _exponentBias); // The fractional part is Big Endian unsigned int to the right of the radix point // So we reverse the bytes and pack them back into an int var fractionBytes = new byte[] { value[3], value[2], value[1], 0 }; // Note: The sign bit for int32 is in the last byte of the array, which is zero, so we don't have to convert to uint var mantissa = BitConverter.ToInt32(fractionBytes, 0); // And divide by 2^(8 * 3) to move the decimal all the way to the left var fraction = mantissa / (float)_threeByteShift; return (float)(sign * exponent * fraction); } /// /// Given a 32-bit IEEE single precision floating point number, returns four bytes encoding /// a single precision number in IBM System/360 Floating Point format /// public static byte[] GetBytes(Single value) { var bytes = new byte[4]; if (value == 0) return bytes; // Sign if (value < 0) bytes[0] = 128; var v = Math.Abs(value); // Fraction // Find the number of digits (in the IBM base) we need to move the radix point to get a value that is less than 1 var moveRadix = (int)Math.Log(v, _ibmBase) + 1; var fraction = v / (Math.Pow(_ibmBase, moveRadix)); var fractionInt = (int)(_threeByteShift * fraction); var fractionBytes = BitConverter.GetBytes(fractionInt); bytes[3] = fractionBytes[0]; bytes[2] = fractionBytes[1]; bytes[1] = fractionBytes[2]; // Exponent var exponent = moveRadix + _exponentBias; bytes[0] += (byte)exponent; return bytes; } #endregion #region Packed Decimal /// /// Unpack the byte array into a decimal /// /// /// From the java version made by p4w3l located here : http://cobol2j.cvs.sourceforge.net/viewvc/cobol2j/cobol2j/src/main/java/net/sf/cobol2j/RecordSet.java?revision=1.25&view=markup from /// the open source projet cobol2j at http://sourceforge.net/projects/cobol2j/ under LGPLV2 /// public static Decimal ToUnpackedDecimal(byte[] inputData, int scale) { var inputLength = inputData.Length; var strbuf = new StringBuilder(); int tempData; int tempData1; for (int i = 0; i < inputData.Length; i++) { tempData = inputData[i]; tempData1 = tempData & 0xF0; int tempData2 = tempData1 >> 4; strbuf.Append(tempData2); if (i < (inputLength - 1)) { tempData = inputData[i]; tempData1 = tempData & 0x0F; strbuf.Append(tempData1); } } if ((scale > 0 && strbuf.Length -scale>0)) strbuf.Insert(strbuf.Length - scale, '.'); var result = decimal.Parse(strbuf.ToString()); tempData = inputData[inputLength - 1]; tempData1 = tempData & 0x0F; if ((tempData1 == 0x0F) || (tempData1 == 0x0C)) return result; if (tempData1 == 0x0D) return -result; return result; } /// /// Convert the decimal value into its packed value /// /// The value to pack /// The packed value as a byte[] /// /// James Howey Copyright (c) Microsoft Corporation. All rights reserved. Microsoft Shared Source Permissive License /// public static byte[] GetBytes(decimal originalValue) { var value = long.Parse(originalValue.ToString(CultureInfo.InvariantCulture).Replace(".", "")); var comp3 = new Stack(10); byte currentByte; if (value < 0) { currentByte = 0x0d; value = -value; } else currentByte = 0x0c; var byteComplete = false; while (value != 0) { if (byteComplete) currentByte = (byte)(value % 10); else currentByte |= (byte)((value % 10) << 4); value /= 10; byteComplete = !byteComplete; if (byteComplete) comp3.Push(currentByte); } if (!byteComplete) comp3.Push(currentByte); return comp3.ToArray(); } #endregion } }