Я наблюдал за сообщениями на многочисленных форумах, посвященных конвертации данных BCD Comp-3 из "устаревших" файлов мэйнфреймов в нечто, пригодное для использования в C #. Во-первых, я хотел бы сказать, что я менее чем восхищен ответами, которые получили некоторые из этих постов, особенно те, в которых, по сути, говорилось «почему вы мешаете нам этими постами, не связанными с C # / C ++», а также «Если Вам нужен ответ о какой-то конвенции COBOL, почему бы вам не посетить сайт, ориентированный на COBOL ". Для меня это полная BS, так как, вероятно, понадобится еще много лет, (к сожалению), чтобы разработчики программного обеспечения поняли, как справляться с некоторыми из этих унаследованных проблем, существующих в РЕАЛЬНОМ МИРЕ. Таким образом, даже если мне захочется написать этот пост из-за следующего кода, я собираюсь поделиться с вами опытом РЕАЛЬНОГО МИРА, с которым мне пришлось иметь дело в отношении преобразования COMP-3 / EBCDIC (и да, именно он говорит о " дискеты, бумажная лента, комплекты дисков и т. д. - я работаю инженером-программистом с 1979 года ").
Сначала - поймите, что любой файл, который вы читаете из устаревшей системы основных фреймов, такой как IBM, собирается представить вам данные в формате EBCDIC, а для преобразования любых этих данных в строку C # / C ++ вы можете обработать с вами придется использовать правильный перевод кодовой страницы, чтобы получить данные в формате ASCII. Хороший пример того, как с этим справиться:
StreamReader readFile = новый StreamReader (путь, Encoding.GetEncoding (037); // 037 = преобразование EBCDIC в ASCII.
Это гарантирует, что все, что вы читаете из этого потока, будет затем преобразовано в ASCII и может быть использовано в строковом формате. Сюда входят поля «Zoned Decimal» (Рис. 9) и «Текст» (Рис. 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);
}
Если у вас есть какие-либо вопросы, опубликуйте их здесь - потому что я подозреваю, что меня "зажгут", как и всех, кто решил публиковать вопросы, относящиеся к сегодняшним вопросам ...
Спасибо,
Джон - Старший.