なお、パック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進数(バイト列)を生成するサンプルです。
ゾーン部+数値部のバイト列を一旦作成し、後から符号部のバイト列を設定しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | /// <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型に変換する際、桁あふれする場合がありますので、業務要件に応じて適切に処理してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | /// <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の場合あり } |
リンク