7

Jon Skeet の .NET での EBCDIC 実装を使用して、メインフレーム システムから FTP を使用してバイナリ モードでダウンロードされた VSAM ファイルを読み取ります。このエンコーディングでの読み取り/書き込みには非常にうまく機能しますが、パック 10 進数値を読み取る機能はありません。私のファイルにはこれらが含まれており、それらを展開する必要があります (明らかに、より多くのバイトを犠牲にして)。

これどうやってするの?

私のフィールドは次のように定義されていますPIC S9(7)V99 COMP-3.

4

2 に答える 2

4

ああ、BCD。6502 アセンブリで使用した場合はクラクションを鳴らしてください。

もちろん、最善の策は、COBOL MOVE に任せることです。これらの可能性のいずれかが役立つ場合があります。

(可能性 #1) メインフレームとソース コードにアクセスでき、出力ファイルが自分専用であると仮定すると、プログラムを変更して、値を単純なアンパック PIC S9(7)V99 に移動するだけにします。

(可能性 #2) それほど簡単ではない場合 (たとえば、ファイルが他の pgms の入力である、またはコードを変更できない場合)、そのファイルを読み取り、別のファイルを書き込む別の COBOL プログラムをシステム上に作成できます。BCD を含むファイル レコード レイアウトを切り取り、入力および出力ファイル用の新しいプログラムに貼り付けます。出力バージョンを非パックに変更します。レコードを読み取り、「対応する移動」を実行してデータを転送し、eof まで書き込みます。次に、そのファイルを転送します。

(可能性 #3) メインフレームに触れられない場合は、コメントでリンクした記事の説明に注意してください。BCD は比較的単純です。次のように簡単にできます (vb.net):

Private Function FromBCD(ByVal BCD As String, ByVal intsz As Integer, ByVal decsz As Integer) As Decimal
    Dim PicLen As Integer = intsz + decsz
    Dim result As Decimal = 0
    Dim val As Integer = Asc(Mid(BCD, 1, 1))
    Do While PicLen > 0
        result *= 10D
        result += val \ 16
        PicLen -= 1
        If PicLen > 0 Then
            result *= 10D
            result += val Mod 16
            PicLen -= 1
            BCD = Mid(BCD, 2)
        End If
        val = Asc(Mid(BCD, 1, 1))
    Loop
    If val Mod 16 = &HD& Then
        result = -result
    End If
    Return result / CDec(10 ^ decsz)
End Function

この呼び出しのいくつかのバリエーションでテストしました。

MsgBox(FromBCD("@" & Chr(13 + 16), 2, 1))

たとえば、-40.1 です。しかし、ほんの少しです。だから、まだ間違っているかもしれません。

したがって、comp-3 が、たとえば入力レコード レイアウトのバイト 10 で始まる場合、これで解決します。

dim valu as Decimal = FromBCD(Mid(InputLine,10,5), 7,2))

送信するバイト数と、V の前後の 9 の数については、データ変換の記事の式に注意してください。

丸め誤差を避けるために、結果を Decimal に格納します。それが$$$の場合は特に。フロート&ダブルはあなたを悲しませます!処理していない場合は、文字列でも良いです。

もちろん、それはもっと難しいかもしれません。私が働いている場所では、メインフレームは 1 バイトあたり 9 ビットです。深刻。それが、最初の 2 つの可能性を際立たせている理由です。もちろん、実際にそれらをより良くするのは、あなたが PC のみのプログラマーである可能性があるという事実です。これは、メインフレーム プログラマーに作業を任せる絶好の言い訳です! 幸運にもその選択肢があれば…

ピース、アル

于 2010-03-05T20:20:32.217 に答える
0

パック 10 進数 (BCD) 変換には、次の拡張メソッドを使用します。

    /// <summary>
    /// computes the actual decimal value from an IBM "Packed Decimal" 9(x)v99 (COBOL COMP-3) format
    /// </summary>
    /// <param name="value">byte[]</param>
    /// <param name="precision">byte; decimal places, default 2</param>
    /// <returns>decimal</returns>
    public static decimal FromPackedDecimal(this byte[] value, byte precision = 2)
    {
        if (value.Length < 1)
        {
            throw new System.InvalidOperationException("Cannot unpack empty bytes.");
        }
        double power = System.Math.Pow(10, precision);
        if (power > long.MaxValue)
        {
            throw new System.InvalidOperationException(
                $"Precision too large for valid calculation: {precision}");
        }
        string hex = System.BitConverter.ToString(value).Replace("-", "");
        var bytes = Enumerable.Range(0, hex.Length)
                 .Select(x => System.Convert.ToByte($"0{hex.Substring(x, 1)}", 16))
                 .ToList();
        long place = 1;
        decimal ret = 0;
        for (int i = bytes.Count - 2; i > -1; i--)
        {
            ret += (bytes[i] * place);
            place *= 10;
        }
        ret /= (long)power;
        return (bytes.Last() & (1 << 7)) != 0 ? ret * -1 : ret;
    }
于 2021-01-21T17:11:24.867 に答える