Я просматривал сообщения на многочисленных форумах о преобразовании данных BCD Comp-3 из «устаревших» файлов мэйнфреймов во что-то, что можно использовать на C#. Во-первых, я хотел бы сказать, что я менее чем в восторге от ответов, которые получили некоторые из этих сообщений, особенно те, в которых говорилось по существу «почему вы беспокоите нас этими сообщениями, не связанными с C#/C++», а также «Если вам нужен ответ о каком-то соглашении COBOL, почему бы вам не посетить сайт, ориентированный на COBOL». Для меня это полная ерунда, поскольку, вероятно, в течение многих лет (к сожалению) разработчикам программного обеспечения будет необходимо понять, как справляться с некоторыми из этих унаследованных проблем, которые существуют в РЕАЛЬНОМ МИРЕ. Итак, даже если меня захлопнут в этом посте за следующий код, я поделюсь с вами РЕАЛЬНЫМ опытом, с которым мне пришлось столкнуться в отношении преобразования COMP-3/EBCDIC (и да, я тот, кто говорит о " дискеты, бумажные ленты, пакеты дисков и т. д. - Я работаю инженером-программистом с 1979 года").
Во-первых, поймите, что любой файл, который вы читаете из устаревшей системы мэйнфреймов, такой как IBM, будет представлять вам данные в формате EBCDIC, и для преобразования любых этих данных в строку C#/C++ вы можете иметь дело с вами. придется использовать правильный перевод кодовой страницы, чтобы получить данные в формате ASCII. Хорошим примером того, как с этим справиться, может быть:
StreamReader readFile = new StreamReader(path, Encoding.GetEncoding(037); // 037 = преобразование EBCDIC в ASCII.
Это гарантирует, что все, что вы читаете из этого потока, будет затем преобразовано в ASCII и может использоваться в строковом формате. Сюда входят поля «Zoned Decimal» (рис. 9) и «Text» (рис. X), как объявлено COBOL. Однако это не обязательно преобразует поля COMP-3 в правильный «двоичный» эквивалент при чтении в массив char[] или byte[]. Чтобы сделать это, единственный способ, которым вы когда-либо собираетесь правильно перевести это (даже с использованием кодовых страниц UTF-8, UTF-16, Default или любых других), вам нужно открыть файл следующим образом:
FileStream fileStream = новый FileStream(путь, FIleMode.Open, FIleAccess.Read, FileShare.Read);
Конечно, опция «FileShare.Read» является «необязательной».
Когда вы изолировали поле, которое хотите преобразовать в десятичное значение (а затем, при необходимости, в строку ASCII), вы можете использовать следующий код, и он был в основном украден из публикации MicroSoft «UnpackDecimal», которую вы можно получить по адресу:
http://www.microsoft.com/downloads/details.aspx?familyid=0e4bba52-cc52-4d89-8590-cda297ff7fbd&displaylang=en
Я выделил (как мне кажется) наиболее важные части этой логики и объединил их в два метода, которые вы можете делать с тем, что хотите. Для моих целей я решил оставить это как возвращающее десятичное значение, которое я затем мог бы сделать с тем, что хотел. По сути, метод называется «распаковать», и вы передаете ему массив byte[] (не длиннее 12 байт) и шкалу в виде целого числа, которое представляет собой количество десятичных разрядов, которое вы хотите вернуть в десятичном значении. Я надеюсь, что это сработает для вас так же, как и для меня.
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);
}
Если у вас есть какие-либо вопросы, задавайте их здесь, потому что я подозреваю, что меня «разозлят», как и всех, кто решил публиковать вопросы, имеющие отношение к сегодняшним проблемам...
Спасибо, Джон - Старейшина.
person
John
schedule
17.04.2012