如何在C#.net中将EBCDIC转换为ASCII

我的EBCDIC格式为“000000 {”。 我想将其转换为.Net Int32类型。 任何人都可以让我知道我能做些什么吗? 所以我的问题是给出一个字符串,其中包含EBCDIC中的带符号数字,我应该怎么做才能将它转换为.NET Int32。

非常感谢提前!

试试这个

#region public static byte[] ConvertAsciiToEbcdic(byte[] asciiData) public static byte[] ConvertAsciiToEbcdic(byte[] asciiData) { // Create two different encodings. Encoding ascii = Encoding.ASCII; Encoding ebcdic = Encoding.GetEncoding("IBM037"); //Retutn Ebcdic Data return Encoding.Convert(ascii, ebcdic, asciiData); } #endregion #region public static byte[] ConvertEbcdicToAscii(byte[] ebcdicData) public static byte[] ConvertEbcdicToAscii(byte[] ebcdicData) { // Create two different encodings. Encoding ascii = Encoding.ASCII; Encoding ebcdic = Encoding.GetEncoding("IBM037"); //Retutn Ascii Data return Encoding.Convert(ebcdic, ascii, ebcdicData); } #endregion 

当从我们的一个客户接收数据时,以下程序用于将EBCDIC值转换为整数。 我们获得的数据可能是您可能获得的数据的一部分,因此请查看这是否适合您:

 using System; using System.Text; namespace ConsoleApplication6 { class Program { static void Main(string[] args) { string strAmount = "00007570{"; Console.WriteLine("{0} is {1}", strAmount, ConvertEBCDICtoInt(strAmount)); strAmount = "000033}"; Console.WriteLine("{0} is {1}", strAmount, ConvertEBCDICtoInt(strAmount)); Console.ReadLine(); } // This converts "00007570{" into "75700", and "000033}" into "-330" public static int? ConvertEBCDICtoInt(string i_strAmount) { int? nAmount = null; if (string.IsNullOrEmpty(i_strAmount)) return(nAmount); StringBuilder strAmount = new StringBuilder(i_strAmount); if (i_strAmount.IndexOfAny(new char[] { '}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R' }) >= 0) strAmount.Insert(0, "-"); strAmount.Replace("{", "0"); strAmount.Replace("}", "0"); strAmount.Replace("A", "1"); strAmount.Replace("J", "1"); strAmount.Replace("B", "2"); strAmount.Replace("K", "2"); strAmount.Replace("C", "3"); strAmount.Replace("L", "3"); strAmount.Replace("D", "4"); strAmount.Replace("M", "4"); strAmount.Replace("E", "5"); strAmount.Replace("N", "5"); strAmount.Replace("F", "6"); strAmount.Replace("O", "6"); strAmount.Replace("G", "7"); strAmount.Replace("P", "7"); strAmount.Replace("H", "8"); strAmount.Replace("Q", "8"); strAmount.Replace("I", "9"); strAmount.Replace("R", "9"); // Convert the amount to a int: int n; if (int.TryParse(strAmount.ToString(), out n)) nAmount = n; return (nAmount); } } } 

您将要阅读二进制编码的小数 ,因为这正是您所面临的问题,并且在您真正编码之前还有一些问题需要回答。

如果值是单个字符,它可能就像获取字符编号一样简单 – 但您需要知道系统是否是Big Endian(与大多数将从中获取EBDIC编码文件的大型机)或Little Endian(像更现代的操作系统)。

如果您的整数值使用多个字符并包含符号(如您所述),则它更复杂。 最有可能的是,每个字符的每一半(或“半字节”或4位)代表数字 – 可能是0到9或hex0到F,并且字符串在左边用零(实际上是空值)填充,最后一个半字节包含符号。 可以用某种说法将该系统称为Zoned Decimal 。

总而言之,我建议首先阅读本文,它将向您介绍如何将数据存储在基于COBOL的大型机上 ,并让您朝着正确的方向前进。


在C#中,您可以通过使用带有正确NumberStyles选项的int.Parse来执行常见的Zoned Decimal转换(这听起来像您描述的那样最适合您的传入数据),如下所示:

 int val = int.Parse(num, NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite); 

一般来说,您应该能够使用正确的System.Text.Encoding类加载EBCDIC数据(该链接指向所有编码的列表,其中包括EBCDIC编码)。 该字符串在内存中是Unicode,可以使用ASCII编码保存为ASCII。

这就是你在问题的标题中所要求的。 但是,我不确定这是否是你想知道的,因为我的问题并不完全清楚。 如果您正在寻找ASCII字符代码,只要它们只是ASCII字符,您就可以将该字符转换为int

这些是我们使用的扩展方法和unit testing:

  ///  /// parses a signed or unsigned decimal in EBCDIC format int an integer ///  ///  ///  private static int? FromZonedDecimalString(this string value) { var trimmed = ("" + value).Trim(); if (trimmed.Length == 0) return null; int testValue; if (Int32.TryParse(trimmed, out testValue)) return testValue; var lastChar = Convert.ToChar(trimmed.Substring(trimmed.Length - 1, 1)); var result = 0; if (trimmed.Length > 1) result = Int32.Parse(trimmed.Substring(0, trimmed.Length - 1)) * 10; switch (lastChar) { case '{': return result; case '}': return -1 * result; default: if (lastChar >= 'A' && lastChar <= 'I') return result + lastChar - 'A' + 1; if (lastChar >= 'J' && lastChar <= 'R') return (result + lastChar - 'J' + 1) * -1; if (lastChar >= '0' && lastChar <= '9') return (result + lastChar - '0' + 1) * -1; break; } return null; } ///  /// converts an integer value into zoned signed EBCDIC decimal format ///  ///  ///  public static string ToZonedSignedDecimalString(this int value) { var str = Math.Abs(value).ToString(); str = str.Substring(0, str.Length - 1); var lastDigit = Math.Abs(value % 10); if (value < 0) { if (lastDigit == 0) return str + "}"; if (lastDigit == 1) return str + "J"; if (lastDigit == 2) return str + "K"; if (lastDigit == 3) return str + "L"; if (lastDigit == 4) return str + "M"; if (lastDigit == 5) return str + "N"; if (lastDigit == 6) return str + "O"; if (lastDigit == 7) return str + "P"; if (lastDigit == 8) return str + "Q"; if (lastDigit == 9) return str + "R"; throw new NotSupportedException("If this throws, I'm at a loss. Last digit was: " + lastDigit); } if (lastDigit == 0) return str + "{"; if (lastDigit == 1) return str + "A"; if (lastDigit == 2) return str + "B"; if (lastDigit == 3) return str + "C"; if (lastDigit == 4) return str + "D"; if (lastDigit == 5) return str + "E"; if (lastDigit == 6) return str + "F"; if (lastDigit == 7) return str + "G"; if (lastDigit == 8) return str + "H"; if (lastDigit == 9) return str + "I"; throw new NotSupportedException("If this throws, I'm at a loss. Last digit was: " + lastDigit); } [TestClass] public class IntExtensionsTests { [TestMethod] public void TestConversion() { string signedDecimalString; int convertedlValue; for (int i = -1000001; i <= 1000001; i++) { signedDecimalString = i.ToZonedSignedDecimalString(); convertedlValue = signedDecimalString.ConvertRightSignedJustifySignedValueToInt(); Assert.AreEqual(i, convertedlValue); } } } 

试试以下function..

 public string ConvertEBCDICtoASCII(string strEBCDICString) { int[] e2a = new int[256]{ 0, 1, 2, 3,156, 9,134,127,151,141,142, 11, 12, 13, 14, 15, 16, 17, 18, 19,157,133, 8,135, 24, 25,146,143, 28, 29, 30, 31, 128,129,130,131,132, 10, 23, 27,136,137,138,139,140, 5, 6, 7, 144,145, 22,147,148,149,150, 4,152,153,154,155, 20, 21,158, 26, 32,160,161,162,163,164,165,166,167,168, 91, 46, 60, 40, 43, 33, 38,169,170,171,172,173,174,175,176,177, 93, 36, 42, 41, 59, 94, 45, 47,178,179,180,181,182,183,184,185,124, 44, 37, 95, 62, 63, 186,187,188,189,190,191,192,193,194, 96, 58, 35, 64, 39, 61, 34, 195, 97, 98, 99,100,101,102,103,104,105,196,197,198,199,200,201, 202,106,107,108,109,110,111,112,113,114,203,204,205,206,207,208, 209,126,115,116,117,118,119,120,121,122,210,211,212,213,214,215, 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231, 123, 65, 66, 67, 68, 69, 70, 71, 72, 73,232,233,234,235,236,237, 125, 74, 75, 76, 77, 78, 79, 80, 81, 82,238,239,240,241,242,243, 92,159, 83, 84, 85, 86, 87, 88, 89, 90,244,245,246,247,248,249, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,250,251,252,253,254,255}; char chrItem = Convert.ToChar("0"); StringBuilder sb = new StringBuilder(); for (int i = 0; i < strEBCDICString.Length; i++) { try { chrItem = Convert.ToChar(strEBCDICString.Substring(i, 1)); sb.Append(Convert.ToChar(e2a[(int)chrItem])); } catch (Exception ex) { Console.WriteLine(ex.Message); return string.Empty; } } string result = sb.ToString(); sb = null; return result; } 

这个问题很老了,但我们最近遇到了同样的问题。 似乎一些大型金融机构(我看着你的Fidelity)仍然使用你需要与之通信的老式大型机系统,这些系统需要分区小数。

我在其他答案中发现的问题是他们使用的字符串操作操作很慢。 我整理了一个简单的C#库,它以数字方式进行转换并将其放在GitHub上。 请查看以下链接,以获得对问题的深入描述。 我已经包含了ZonedDecimalConverter类的(当前的当前)实现。

GitHub上的zoned-decimal

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ZonedDecimal { public static class ZonedDecimalConverter { public enum RoundingOperation { AwayFromZero, ToEven, Truncate }; const byte MASK_UNSIGNED = 0xF0; const byte MASK_POSITIVE = 0xC0; const byte MASK_NEGATIVE = 0xD0; // this is a subset of the IBM code page 37 EBCDIC character set. we are only concerned with the characters that correspond to numbers. // for this dictionary, you key in with the code and get the character static readonly Dictionary m_IBM037Characters = new Dictionary() { {0xC0, '{'},{0xC1, 'A'},{0xC2, 'B'},{0xC3, 'C'},{0xC4, 'D'},{0xC5, 'E'},{0xC6, 'F'},{0xC7, 'G'},{0xC8, 'H'},{0xC9, 'I'} ,{0xD0, '}'},{0xD1, 'J'},{0xD2, 'K'},{0xD3, 'L'},{0xD4, 'M'},{0xD5, 'N'},{0xD6, 'O'},{0xD7, 'P'},{0xD8, 'Q'},{0xD9, 'R'} ,{0xF0, '0'},{0xF1, '1'},{0xF2, '2'},{0xF3, '3'},{0xF4, '4'},{0xF5, '5'},{0xF6, '6'},{0xF7, '7'},{0xF8, '8'},{0xF9, '9'} }; // this is the inverse of the dictionary above. you key in with the character and get the code static readonly Dictionary m_IBM037Codes = m_IBM037Characters.ToDictionary((pair) => pair.Value, (pair) => pair.Key); ///  /// Returns a string that represents the zone-decimal version of the value specified ///  /// The value /// How many fixed digits should be to the left of the decimal place /// How many fixed digits should be to the right of the decimal place /// Indicates how to handle decimal digits beyond those specified by digitsRight ///  public static string GetZonedDecimal(decimal value, int digitsLeft, int digitsRight, RoundingOperation roundingOperation) { // bounds checking if (digitsLeft < 1) throw new ArgumentException("Value must be greater than zero.", "digitsLeft"); if (digitsRight < 0) throw new ArgumentException("Value must be greater than or equal to zero.", "digitsRight"); // zoned-decimal has its own way of signaling negative bool isNegative = false; if (value < 0) { isNegative = true; value = -value; // same result as Math.Abs } // apply any rounding operation if (roundingOperation != RoundingOperation.Truncate) value = Math.Round(value, digitsRight, roundingOperation == RoundingOperation.AwayFromZero ? MidpointRounding.AwayFromZero : MidpointRounding.ToEven); /* calculating with decimal is extremely slow comapred to int. we'll multiple the number by digitsRight to put all significant * digits into whole number places and then load it into an unsigned long. since ulong.MaxValue is 18446744073709551615, * this gives us 20 digits total to work with. assuming you used 4 digits to the right, you could have up to 16 to the left, etc. * we do not use uint here since uint.MaxValue is 4294967295 and that would only give us 10 digits to work with. many fields * that i have seen have a COBOL signature of S9(11)V99, which is 13 digits total. also, we use unsigned because the sign bit * is not used (zoned-decimal has it own way of signaling negative) and long.MaxValue (vs ulong.MaxValue) is one digit shorter. * if the value is too big to be represented as a ulong with an implied decimal place (not likely) then you're out of luck and * you'll get an exception here */ ulong workingValue = (ulong)(value * (int)Math.Pow(10, digitsRight)); // the total number of digits that will be output int length = digitsLeft + digitsRight; // more bounds checking (eg digitsLeft = 3; digitsRight = 2; if number with implied decimal place > 10^5-1=99999 then it will not fit) if (workingValue > Math.Pow(10, length) - 1) throw new ArgumentException("Value exceeds specified total number of fixed digits.", "value"); // each character will be a digit of the number char[] output = new char[length]; // loop through the number and output each digit as zoned-decimal for (int i = 0; i < length; i++) { byte digit = 0; // if we run out of digits then we'll just keep looping, padding the specified fixed number // of decimal places with zeros if (workingValue > 0) { // current digit is the one that occupies the right-most place digit = (byte)(workingValue % 10); // shift all values to the right, dropping the current right-most value in the process workingValue /= 10; } // the sign indicator is included in the initial right-most digit only if (i == 0) digit |= isNegative ? MASK_NEGATIVE : MASK_POSITIVE; else digit |= MASK_UNSIGNED; // set values of our character array from right to left based on the IBM code page 37 EBCDIC character set output[length - i - 1] = m_IBM037Characters[digit]; } return new string(output); } ///  /// Returns a decimal from a zoned-decimal ///  /// The zoned-decimal string /// Number of digits that should be to the right of the decimal place ///  public static decimal GetDecimal(string zonedDecimalString, int digitsRight) { // we'll do most calculations with ulong since it's significantly faster then calculating with decimal ulong value = 0; // we'll need a way to determine if the number is negative. this will be signaled in the zone of the right-most character bool isNegative = false; // this will be used to create the place value of each digit ulong multipler = 1; // start at the right-hand side of the number and proceed to the left int lastIndex = zonedDecimalString.Length - 1; for (int i = lastIndex; i >= 0; i--) { // get the EBCDIC code for the character at position i if (!m_IBM037Codes.TryGetValue(zonedDecimalString[i], out byte digit)) throw new ArgumentException("Invalid numeric character found in zoned-decimal string", "zonedDecimalString"); // the right-most character will carry the sign if (i == lastIndex) isNegative = (digit & 0xF0) == MASK_NEGATIVE; // strip out the zone digit &= 0x0F; // add the place value of the digit to our return value value += digit * multipler; // multipler goes to the next "place" (tens/hundreds/thousands/etc) multipler *= 10; } // now we're going to deal with decimal places and negatives, so we have to switch to a decimal decimal returnValue = value; // deal with digits to the right of the decimal if (digitsRight > 0) returnValue /= (int)Math.Pow(10, digitsRight); // deal with negative if (isNegative) returnValue = -returnValue; return returnValue; } } }