ゾーン10進数の変換方法とC#のサンプル

なお、パック10進数はこちらで紹介しています。

ゾーン10進数

  • ゾーン10進数の変換方法
    • 10進数の各桁を「ゾーン部(上位4ビット)+数値部(下位4ビット)」のバイトに変換して生成します。
    • 生成したバイト列の最後は「符号部(上位4ビット)+数値部(下位4ビット)のバイトを設定します。
    • 生成したゾーン10進数が実行環境で可読文字(“0”,”1″等の文字列)として扱えるよう、想定する文字コードによってゾーン部・符号部の値が異なります。
  • 文字コード毎のゾーン部、符号部の値は次の通りです。
    • 使用される文字コードはASCIIコード、EBCDICコードのいずれかになることが多いため、ゾーン部の値はほぼ統一されています。
    • 符号部に関しては、ベンダ依存になっており統一されていません。
    • 業務で使用する場合は、使用する文字コード、ゾーン部や符号部の値を確認することをおすすめします。
    • 文字コードゾーン部符号部
      ASCII3 (0b0011=3)無: 3 (0b0011=3)
      +: 3 (0b0011=3)
      -: 7 (0b0111=7)
      EBCDICF (0b1111=15)無: F (0b0011=15)
      +: C (0b1100=12)
      -: D (0b1101=13)
      ※IBM汎用機の例
    • 各文字コードにおける変換例を次に示します。
      文字コードサンプル(整数)ゾーン10進数(16進数)文字コード上の文字備考
      ASCII12331 32 33“123”
      -12331 32 73“12s”
      EBCDIC123F1 F2 C3“12C”符号部は前述のIBM汎用機を想定
      -123F1 F2 D3“12L”符号部は前述のIBM汎用機を想定
    • その他
      • より少ないバイト数でより多くの桁数を表現するためにパック10進数が登場しました。このパック10進数と対比して、ゾーン10進数はアンパック10進数と呼ばれる場合もあります。

    C#によるサンプル

    • .NET Core 3.1(C# 8.0)を使ったサンプルとなります。
    • 実現方式として、算術演算・ビット演算を使用して数値ベースで変換を行う方式と、文字列操作で変換を行う方式が考えられます。ここでは、単純で分かりやすい、後者の「文字列操作で変換を行う方式」を使っています。
    • 完全なソースコードはこちらで公開しています。

    整数をゾーン10進数に変換

    long型からASCIIコードのゾーン10進数(バイト列)を生成するサンプルです。
    ゾーン部+数値部のバイト列を一旦作成し、後から符号部のバイト列を設定しています。

    /// <summary>
    /// ゾーン10進数: ゾーン部(ASCIIコード使用時)
    /// </summary>
    private const char ZonedDecAsciiZonePart = '3'; // 0b_0011
    
    /// <summary>
    /// ゾーン10進数: 符号部 符号なし・正(ASCIIコード使用時)
    /// </summary>
    private const char ZonedDecAsciiSignPartPlus = '3'; // 0b_0011
    
    /// <summary>
    /// ゾーン10進数: 符号部 負(ASCIIコード使用時)
    /// </summary>
    private const char ZonedDecAsciiSignPartMinus = '7'; // 0b_0111
    
    
    /// <summary>
    /// 整数からゾーン10進数を生成する。
    /// </summary>
    /// <param name="num">整数</param>
    /// <returns>ゾーン10進数</returns>
    public static byte[] CreateZonedDecimalBytes(long num)
    {
        var isPositive = num >= 0;
        var numstr = num.ToString().TrimStart('-'); // -123 => 123
        var sb = new StringBuilder();
    
        // 最後を除く16進数2桁の生成
        foreach (var c in numstr[0..^1])
            sb.Append(ZonedDecAsciiZonePart).Append(c);
    
        // 最後の16進数2桁の生成
        var sign = isPositive ? ZonedDecAsciiSignPartPlus : ZonedDecAsciiSignPartMinus;
        sb.Append(sign).Append(numstr[^1]);
    
        // 文字列の各2桁を16進数としてバイトに変換する。
        var str = sb.ToString();
        return Enumerable.Range(0, str.Length)
            .Where(i => i % 2 == 0)
            .Select(i => Convert.ToByte(str.Substring(i, 2), 16))
            .ToArray();
    }

    ゾーン10進数を整数に変換

    ASCIIコードのゾーン10進数(バイト列)からlong型の整数を生成するサンプルです。
    バイト列の数値部から数字を取り出して整数文字列を生成し、最後に文字列からlong型に変換します。
    ゾーン部、符号部がASCIIコードで想定している値以外の場合は例外をスローしています。
    整数文字列をlong型に変換する際、桁あふれする場合がありますので、業務要件に応じて適切に処理してください。

    /// <summary>
    /// ゾーン10進数: ゾーン部(ASCIIコード使用時)
    /// </summary>
    private const char ZonedDecAsciiZonePart = '3'; // 0b_0011
    
    /// <summary>
    /// ゾーン10進数: 符号部 符号なし・正(ASCIIコード使用時)
    /// </summary>
    private const char ZonedDecAsciiSignPartPlus = '3'; // 0b_0011
    
    /// <summary>
    /// ゾーン10進数: 符号部 負(ASCIIコード使用時)
    /// </summary>
    private const char ZonedDecAsciiSignPartMinus = '7'; // 0b_0111
    
    /// <summary>
    /// ゾーン10進数から整数を生成する。
    /// </summary>
    /// <param name="bytes">ゾーン10進数</param>
    /// <returns>整数</returns>
    public static long ParseZonedDecimal(byte[] bytes)
    {
        var hexstr = BitConverter.ToString(bytes).Replace("-", ""); // 1F-34 => 1F34
        var sb = new StringBuilder();
        for (var i = 0; i < hexstr.Length; i+=2)
        {
            // 上位4ビット(ゾーン部or符号部)の検証
            var u4 = hexstr[i];
            if (i < hexstr.Length - 2)
            {
                // 最終バイト以外は期待するゾーン部であることを検証
                if (u4 != ZonedDecAsciiZonePart)
                    throw new ArgumentException($"不正なゾーン部: {hexstr}");
            }
            else
            {
                // 最終バイトは符号部であることを検証
                if (u4 == ZonedDecAsciiSignPartMinus)
                    sb.Insert(0, "-");
                else if (u4 != ZonedDecAsciiSignPartPlus)
                    throw new ArgumentException($"不正な符号部: {hexstr}");
            }
    
            // 下位4ビットは数値(0-9)であることを検証
            var l4 = hexstr[i + 1];
            if (l4 < '0' || '9' < l4)
                throw new ArgumentException($"不正な数値部: {hexstr}");
    
            sb.Append(l4);
        }
        return long.Parse(sb.ToString()); // OverflowExceptionの場合あり
    }