C#を使用した開発現場において、数値の基数変換は避けて通れない重要な技術要素の一つです。
特に組み込みデバイスとの通信やバイナリデータの解析、暗号化処理、画像処理などの分野では、2進数、10進数、16進数を自在に操るスキルが求められます。
近年のC#はハイパフォーマンスな記述が容易になっており、メモリ効率を意識した変換手法も広く普及しています。
この記事では、C#の標準ライブラリを用いた基本的な変換方法から、実戦で即戦力となる応用パターン、さらには最新のパフォーマンス最適化手法までを詳しく解説します。
Convertクラスを活用した汎用的な変換手法
C#において最も標準的かつ直感的な変換方法は、System.Convertクラスを利用することです。
このクラスには、数値を特定の基数(2進数、8進数、10進数、16進数)に基づく文字列へ変換したり、その逆を行ったりするためのメソッドが豊富に用意されています。
10進数から2進数および16進数への変換
整数を文字列に変換する際は、Convert.ToStringメソッドを使用します。
第2引数に基数(2、8、10、16)を指定することで、対象の数値表現を簡単に得ることが可能です。
using System;
int decimalValue = 255;
// 10進数から2進数文字列へ変換
string binaryString = Convert.ToString(decimalValue, 2);
// 10進数から16進数文字列へ変換
string hexString = Convert.ToString(decimalValue, 16);
Console.WriteLine($"10進数: {decimalValue}");
Console.WriteLine($"2進数: {binaryString}");
Console.WriteLine($"16進数: {hexString}");
10進数: 255
2進数: 11111111
16進数: ff
この方法の利点は、非常にシンプルなコードで多種の基数に対応できる点にあります。
一方で、16進数の場合にアルファベットが大文字にならない、あるいは桁数を固定するパディング処理(0埋め)が自動で行われないという特徴があります。
2進数・16進数文字列から10進数への変換
文字列として表現された数値を整数型に戻す場合は、Convert.ToInt32メソッドを使用します。
この場合も、第2引数に「元の文字列が何進数であるか」を明示的に指定する必要があります。
string binaryInput = "101010";
string hexInput = "2A";
// 2進数文字列を10進数整数へ変換
int fromBinary = Convert.ToInt32(binaryInput, 2);
// 16進数文字列を10進数整数へ変換
int fromHex = Convert.ToInt32(hexInput, 16);
Console.WriteLine($"2進数 '{binaryInput}' -> 10進数: {fromBinary}");
Console.WriteLine($"16進数 '{hexInput}' -> 10進数: {fromHex}");
2進数 '101010' -> 10進数: 42
16進数 '2A' -> 10進数: 42
文字列に不正な文字が含まれている場合や、数値の範囲を超えている場合は例外が発生するため、実戦では入力値のバリデーションが重要になります。
数値書式指定子を用いた高度な16進数変換
実務で16進数を扱う場合、単なる変換だけでなく「0埋め」や「大文字・小文字の制御」が必要になる場面が多くあります。
このようなケースでは、ToStringメソッドに書式指定子を渡す方法が最も効率的です。
ToString(“X”)によるフォーマット
数値型が持つToStringメソッドに “X” または “x” を指定することで、数値を16進数文字列として出力できます。
大文字の “X” を指定すれば結果は大文字に、小文字の “x” を指定すれば小文字になります。
int value = 1024;
// 16進数大文字で出力
string hexUpper = value.ToString("X");
// 16進数小文字、かつ8桁で0埋め
string hexPadded = value.ToString("x8");
Console.WriteLine($"大文字: {hexUpper}");
Console.WriteLine($"8桁小文字: {hexPadded}");
大文字: 400
8桁小文字: 00000400
この書式指定子は、ログ出力やデバッグ情報の表示、あるいは設定ファイルへの書き出しにおいて非常に重宝されます。
TryParseとNumberStylesの組み合わせ
16進数文字列を数値に変換する際、より安全かつ柔軟に処理を行うにはint.TryParseとSystem.Globalization.NumberStylesを組み合わせる手法が推奨されます。
これにより、16進数特有の接頭辞(0x)が含まれている場合でも適切にパースすることが可能になります。
using System.Globalization;
string hexWithPrefix = "0x7F";
// 接頭辞を考慮してパース
if (int.TryParse(hexWithPrefix.Replace("0x", ""), NumberStyles.HexNumber, null, out int result))
{
Console.WriteLine($"変換成功: {result}");
}
なお、C#の最新バージョンではNumberStyles.AllowHexSpecifierなどを用いることで、空白の混入にも対応できるようになっています。
C#のリテラル表記によるプログラム内での定義
変換処理そのものではありませんが、ソースコード上で2進数や16進数を直接記述する方法を知っておくと、可読性が大幅に向上します。
C#では、数値リテラルに特定の接頭辞を付けることで、10進数以外の値を直接代入できます。
バイナリリテラルと16進数リテラル
2進数は 0b、16進数は 0x を頭に付けて記述します。
// 2進数リテラル(10進数の10)
int bin = 0b1010;
// 16進数リテラル(10進数の255)
int hex = 0xFF;
// 桁区切り文字(_)を利用して読みやすく記述
int largeBin = 0b1100_1010_1111_0000;
Console.WriteLine($"bin: {bin}, hex: {hex}, largeBin: {largeBin}");
特にビットフラグを定義する際や、プロトコル仕様書に基づいた定数定義を行う場合には、10進数よりもこれらのリテラル表記の方が直感的です。
桁区切り文字 _ はコンパイル時に無視されるため、ビットのまとまりを視覚的に表現するのに役立ちます。
パフォーマンスを追求した変換実装
大量のデータを高速に変換する必要がある場合、文字列生成によるガベージコレクション(GC)の発生を最小限に抑える必要があります。
最新のC#では、Span<T>型やUtf8Formatterを活用することで、メモリ効率の極めて高い変換が可能です。
Span<char>を用いたメモリ節約型の変換
ISpanFormattableインターフェースを利用することで、スタックメモリ上に確保したバッファへ直接数値を書き込むことができます。
これにより、余計なstringオブジェクトの生成を回避できます。
using System;
int value = 30000;
Span<char> buffer = stackalloc char[8];
if (value.TryFormat(buffer, out int charsWritten, "X"))
{
ReadOnlySpan<char> result = buffer.Slice(0, charsWritten);
Console.WriteLine($"バッファへの書き込み成功: {result.ToString()}");
}
高頻度で実行されるループ処理内での変換では、このような低レイヤの最適化がアプリケーション全体のパフォーマンスに大きく寄与します。
実戦で役立つ変換ユーティリティの例
最後に、現場でよく使われる「バイト配列(byte[])と16進数文字列の相互変換」を紹介します。
これは通信パケットの内容を確認したり、ハッシュ値を表示したりする際に必須となる処理です。
バイト配列から16進数への一括変換
BitConverterクラスを使用する方法もありますが、C# 5.0以降であればConvert.ToHexString(.NET 5以降)を利用するのが最も高速で簡潔です。
byte[] data = { 0x00, 0x1A, 0xFF, 0x7E };
// バイト配列を16進数文字列(大文字)に一括変換
string hexData = Convert.ToHexString(data);
Console.WriteLine($"バイト配列の16進数表現: {hexData}");
バイト配列の16進数表現: 001AFF7E
逆に、16進数文字列をバイト配列に戻す場合はConvert.FromHexStringメソッドを使用します。
これらのメソッドは内部的に最適化されており、自前でループを回して変換するよりも遥かに高速です。
まとめ
C#における2進数・10進数・16進数の変換は、用途に応じて最適な手法を選択することが重要です。
簡易的な変換であればConvertクラスや数値型のToStringメソッドで十分対応できます。
一方で、特定のフォーマットが求められる場合は書式指定子を、パフォーマンスが要求されるシステムではSpan<T>や最新の専用メソッドを活用してください。
特に0bや0xといったリテラル表記と、Convert.ToHexStringなどの便利なメソッドを組み合わせることで、コードの可読性と実行速度を両立させることができます。
基数変換の仕組みを正しく理解し、型安全かつ効率的なC#プログラミングを実践していきましょう。
