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. システムエンジニアリング, 実装技術, 設計技術

関連記事

ASP.NET Core: Remote属性の過剰なHTTP要求を抑制

Remote属性は入力欄に対する文字入力やフォーカスが外れたタイミングで、サーバ側検証ロジックを実行できます。例えば、登録対象となるユーザIDが既に実在しないかの検証等、サーバ側でしか検証が難しい場合 …

VBAでケバブ・スネーク・パスカル・キャメル変換

開発の現場では、Excelに定義したクラスやテーブル等の設計内容に基づいて、VBAで自動的にソースコードやSQL文等の成果物を生成したい場合があります。この処理を実装する場合、Excel上の項目名を、 …

ExcelからPowerPointへの図表貼り付けVBA

パフォーマンスモニタの監視データ(blg)に基づいてPowerPointで報告用のレポートを作成する必要がありました。パフォーマンスログのデータをCSVに変換してExcelに取り込んでグラフを作成し、 …

開発・検証用のFTPサーバ(IIS)を構築

FTPで外部連携するためのプログラムを開発する際に、接続先のFTPサーバの準備に困る場合があります。 ここでは、Widnwos10上に開発・検証用のFTPサーバを構築するための手順を説明します。 「I …

区分データ生成ツール

区分定義書の変更時、定数や列挙体、マスタの一部を手動で修正するような運用だと、抜け漏れや間違いが発生する場合があります。この辺の手間や間違いを低減するために、区分定義書から自動的に成果物を作成するEx …