NDW

アプリ開発やトラブルシューティング等のノウハウ、キャンプや登山の紹介や体験談など。

.NET Core 1. システムエンジニアリング 実装技術 設計技術

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

投稿日:2021年7月3日 更新日:

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

ゾーン10進数

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







-.NET Core, 1. システムエンジニアリング, 実装技術, 設計技術

関連記事

Visual Studio 2019と開発用DB(LocalDB)

Visual Studio 2019でのASP.NET Coreを使ったシステム開発の標準化を検討している。 開発工程では技術検証、新機能向けのテーブル定義の検討、単体試験等を目的として、開発者が自由 …

ルートCA、サーバ・クライアント証明書の作成方法

概要 検証・開発環境での使用を前提とした、ルートCA証明書の作成、その証明書で署名したサーバ証明書とクライアント証明書を作成する手順を説明します。 可能な限りシンプルで簡単な条件・手順になっています。 …

おすすめキーボード: MX Keys(KX800)レビュー

キーが浅く指の移動が最小限で抑えられ、多少打鍵位置がずれても正確に押せるので、入力がとても楽で、肩こりや首の痛みが軽減されました。また、在宅勤務用PCや副業用PC等の複数PCの切り替えをキーボードから …

JSFラジオボタン・チェックボックスとbootstrap

boostrapでラジオボタンやチェックボックスを使用する場合、div, input, label要素にbootstrapのCSSクラスを指定する必要がある。 JSFでラジオボタンを使用する場合、se …

slf4jとlog4j2を使たデバッグログの出力方法

はじめに Webアプリやスタンドアロンアプリの開発でデバッグログやトレースログを出したい場合があります。 とりあえず、ロガーのログレベルをdebugやtraceに下げればいいや、と設定してもログが出力 …