NDW

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

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

パック10進数の変換方法とC#のサンプル

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

なお、ゾーン10進数はこちらで紹介しています。

パック10進数

  • ゾーン10進数の変換方法
    • 10進数の各桁を「上位4ビットで1桁、下位4ビットで1桁」のバイトに変換して生成します。
    • 生成したバイト列の最後の下位4ビットが符号部になっており、符号を示す値を設定します。
      符号部の値は実行環境に依存する場合があります。IBM汎用機の場合、符号無しはF(1111)、正はC(1100)、負はD(1101)となります。
  • 各文字コードにおける変換例を次に示します。
    サンプル(整数) パック10進数(16進数)
    123 12 3C
    -123 12 3D
    1234 01 23 4C

C#によるサンプル

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

整数をパック10進数に変換

long型からパック10進数(バイト列)を生成するサンプルです。
整数を2桁毎に分割して16進数文字列を作成し、バイト列を生成しています。
符号があるlong型の使用を想定しているので、符号無しF(1111)は使用していません。

/// <summary>
/// パック10進数: 符号部 正(ASCIIコード使用時)
/// </summary>
private const char PackedDecSignPartPlus = 'C'; // 0b_1100

/// <summary>
/// パック10進数: 符号部 負(ASCIIコード使用時)
/// </summary>
private const char PackedDecSignPartMinus = 'D'; // 0b_1101

/// <summary>
/// 整数からパック10進数を生成する。
/// </summary>
/// <param name="num">整数</param>
/// <returns>パック10進数</returns>
public static byte[] CreatePackedDecimalBytes(long num)
{
    var isPositive = num >= 0;
    var sb = new StringBuilder(num.ToString().TrimStart('-')); // -123 => 123

    // 偶数桁の16進数文字列になるよう0を追加
    // (後で符号部1桁を追加するので、ここでは奇数である必要がある。)
    if (sb.Length % 2 == 0) sb.Insert(0, '0');

    // 符号部1桁を追加(16進数)
    sb.Append(isPositive ? PackedDecSignPartPlus : PackedDecSignPartMinus);

    // 文字列の各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進数を整数に変換

パック10進数(バイト列)からlong型の整数を生成するサンプルです。
バイト列から16進数文字列を生成し、符号部を除いてそのまま10進数文字列としてlong型に変換します。
16進数文字列が数値以外(A-F)や、符号部が想定している値以外の場合は例外をスローしています。
整数文字列をlong型に変換する際、桁あふれする場合がありますので、業務要件に応じて適切に処理してください。

/// <summary>
/// パック10進数: 符号部 正(ASCIIコード使用時)
/// </summary>
private const char PackedDecSignPartPlus = 'C'; // 0b_1100

/// <summary>
/// パック10進数: 符号部 負(ASCIIコード使用時)
/// </summary>
private const char PackedDecSignPartMinus = 'D'; // 0b_1101

/// <summary>
/// パック10進数から整数を生成する。
/// </summary>
/// <param name="bytes">パック10進数</param>
/// <returns>整数</returns>
public static long ParsePackedDecimal(byte[] bytes)
{
    // 16進数文字列を生成
    var hexes = BitConverter.ToString(bytes).Replace("-", ""); // 1F-34 => 1F34

    // 数値以外(a-f)が含まれないことを検証
    var numstr = hexes[0..^1];
    foreach (var c in numstr)
        if (c < '0' || '9' < c) throw new ArgumentException($"不正な数値部: {hexes}");

    // 符号部が想定される符号値であることを検証
    var signstr = hexes[^1];
    if (signstr == PackedDecSignPartMinus)
        numstr = "-" + numstr;
    else if (signstr != PackedDecSignPartPlus)
        throw new ArgumentException($"不正な符号部: {hexes}");

    return long.Parse(numstr); // OverflowExceptionの場合あり
}







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

関連記事

テキストファイルマスクツール

本番環境での性能検証でNGになってしまった。 どの処理でどれほどの処理時間がかかっているかを把握するためにログレベルを変更して、ログを取得した。 対応方法を自社の担当者と検討するために、本番環境からこ …

ASP.NET Core: 日本語の文字化け

ダイジェスト Visual Studio 2019で作成したASP.NET Coreプロジェクトで、プログラム(Razor)から日本語を出力するとHTMLエンコードされてしまいます。例えば「さしすせそ …

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

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

ASP.NET Core: IHttpClientFactoryの単純サンプル

IHttpClientの使い方やサンプルの記事を書きましたが、後から見るとちょっと量が多いと感じました。 とりあえず動かしてみたい、概略を知りたい、急いでいる等の人向けに、もっと単純なサンプルを用意し …

Excel VBAで独自形式のCSVファイルを作成

概要 ExcelはRFC4180に準拠したCSV出力が可能ですが、逆にRFC4180に準拠しない独自形式のCSV出力はできません。 そのため、ここではExcel VBAを使って独自のCSVファイルを出 …