C#の16進数変換・表記を完全解説|文字列・数値の変換方法とよくあるエラー対策

はじめに

C#で16進数を扱う場面は、思った以上に多くあります。たとえば、カラーコード、文字コード、バイト配列、ファイルのバイナリデータ、通信データ、ハッシュ値、デバッグログなどでは、数値やバイナリを16進数で表すと読みやすくなります。

C#では、16進数の数値リテラルを書く方法、数値を16進数文字列へ変換する方法、16進数文字列を数値へ戻す方法、byte配列と16進数文字列を相互変換する方法が用意されています。ただし、「0x付きの文字列がParseできない」「負の数を16進数にすると想定外の表示になる」「奇数桁の16進数文字列で例外が出る」といったつまずきもよくあります。

この記事では、C#の16進数変換・表記について、基本から実務で使いやすい実装例までまとめて解説します。

1. C#の16進数とは?基本の考え方と使いどころ

1-1. 16進数の基礎:0〜9とA〜Fで数値を表す仕組み

16進数は、0から15までの値を1桁で表す数値表現です。10進数では0〜9の10種類の数字を使いますが、16進数では0〜9に加えてA〜Fを使います。

10進数16進数
00
11
99
10A
11B
12C
13D
14E
15F
1610

たとえば、10進数の255は16進数ではFFです。これは、15×16 + 15 = 255 になるためです。

C#
int value = 255;
Console.WriteLine(value.ToString("X")); // FF

C#で「c# 16進数」と調べると、多くの場合はこのような数値と文字列の変換方法を知りたいケースが多いです。

1-2. C#で16進数が使われる主な場面

C#で16進数が使われる代表的な場面は次のとおりです。

用途
カラーコード#FF00000xFF0000
バイト配列byte[]0A1B2Cのように表示
文字コードUnicodeのU+3042など
ファイル解析バイナリデータの16進数ダンプ
通信データパケットやプロトコルの確認
ハッシュ値SHA256などの結果表示
デバッグフラグ値やメモリ値の確認

16進数は、2進数より短く、10進数よりビット単位の意味を把握しやすいという特徴があります。

1-3. 10進数・2進数・16進数の違い

同じ値でも、表記方法によって見え方が変わります。

10進数2進数16進数
101010A
151111F
161000010
25511111111FF

16進数の1桁は、2進数の4桁に対応します。

F = 1111
FF = 1111 1111

そのため、バイト単位のデータを表すときは16進数がよく使われます。1バイトは8ビットなので、16進数では2桁で表せます。

byte 255 = 0xFF
byte 0 = 0x00

1-4. 大文字・小文字のA〜Fに違いはあるか

16進数では、A〜Fを大文字で書いても小文字で書いても意味は同じです。

FF = ff
A0 = a0

C#で数値を16進数文字列に変換するときは、書式指定子によって大文字・小文字を切り替えられます。

C#
int value = 255;

Console.WriteLine(value.ToString("X")); // FF
Console.WriteLine(value.ToString("x")); // ff

一般的には、ログやバイナリ表示では大文字のXがよく使われますが、小文字で統一しても問題ありません。大切なのは、プロジェクト内で表記を統一することです。

1-5. 16進数に関するC#の代表的な処理一覧

C#で16進数を扱う主な処理は次のとおりです。

やりたいこと主な方法
16進数リテラルを書く0xFF
数値を16進数文字列にするvalue.ToString("X")
桁数を揃えるvalue.ToString("X2")value.ToString("X8")
16進数文字列をintにするConvert.ToInt32(hex, 16)
安全に変換するint.TryParse(hex, NumberStyles.HexNumber, ...)
byte[]を16進数文字列にするConvert.ToHexString(bytes)
16進数文字列をbyte[]に戻すConvert.FromHexString(hex)
古い環境でbyte[]を変換するBitConverter.ToString(bytes)

2. C#で16進数を表記する方法

2-1. 16進数リテラルは「0x」を付けて書く

C#のコード内で16進数の数値リテラルを書く場合は、先頭に0xまたは0Xを付けます。

C#
int value1 = 0xFF;
int value2 = 0XFF;

Console.WriteLine(value1); // 255
Console.WriteLine(value2); // 255

0xFFは見た目は16進数ですが、変数に入った後は通常の数値です。つまり、int value = 0xFF;と書いても、内部的には10進数の255と同じ値を持ちます。

C#
int a = 255;
int b = 0xFF;

Console.WriteLine(a == b); // True

2-2. int・long・byteなど型ごとの16進数表記例

C#では、さまざまな整数型で16進数リテラルを使えます。

C#
byte b = 0xFF;
short s = 0x7FFF;
int i = 0x7FFFFFFF;
long l = 0x7FFFFFFFFFFFFFFF;
uint ui = 0xFFFFFFFF;
ulong ul = 0xFFFFFFFFFFFFFFFF;

ただし、型の範囲を超える値は代入できません。

C#
byte b1 = 0xFF;  // OK: 255
// byte b2 = 0x100; // エラー: byteの範囲を超える

byteは0〜255までの値しか扱えないため、0x100、つまり10進数の256は代入できません。

2-3. 2進数リテラル「0b」との違い

C#では、16進数リテラルのほかに2進数リテラルも使えます。2進数の場合は先頭に0bまたは0Bを付けます。

C#
int hex = 0xFF;        // 16進数
int bin = 0b11111111; // 2進数

Console.WriteLine(hex); // 255
Console.WriteLine(bin); // 255

2進数はビット単位の意味を細かく見たいときに便利です。一方、16進数は短く書けるため、バイト列やカラーコードなどでよく使われます。

C#
int flags = 0b0001_0010;
int color = 0xFF_00_00;

2-4. 桁区切り「_」を使った読みやすい書き方

C#では、数値リテラルの中に_を入れて桁を区切れます。これは数値の意味には影響せず、読みやすくするための記法です。

C#
int value = 0xFF_EC_DE_5E;

Console.WriteLine(value);

バイト単位で区切ると、16進数の値を読みやすくできます。

C#
uint color = 0xFF_33_66_CC;

この例では、ARGBのように1バイトごとのまとまりが見やすくなります。

2-5. 文字コードやUnicodeエスケープで使う16進数表記

C#では、文字コードを表す場面でも16進数が使われます。

C#
char c = '\u3042';

Console.WriteLine(c); // あ

\u3042は、UnicodeのコードポイントU+3042を表します。これはひらがなの「あ」です。

サロゲートペアを含む文字など、より大きなコードポイントを扱う場合は\Uを使います。

C#
string emoji = "\U0001F600";

Console.WriteLine(emoji); // 😀

また、文字列中で\xを使うこともできますが、読み取り桁数が文脈によって変わることがあるため、Unicode文字を明確に書きたい場合は\u\Uを使うほうが安全です。

3. 数値を16進数文字列に変換する方法

3-1. ToString("X")で数値を16進数に変換する

C#で数値を16進数文字列に変換する基本は、ToString("X")です。

C#
int value = 255;

string hex = value.ToString("X");

Console.WriteLine(hex); // FF

"X"は16進数形式で文字列化するための標準数値書式指定子です。整数型に対してよく使われます。

C#
int value = 48879;

Console.WriteLine(value.ToString("X")); // BEEF

3-2. ToString("x")とToString("X")の違い

"X"を使うとA〜Fが大文字になり、"x"を使うと小文字になります。

C#
int value = 255;

Console.WriteLine(value.ToString("X")); // FF
Console.WriteLine(value.ToString("x")); // ff

どちらを使っても値としての意味は同じです。ログや仕様書に合わせて使い分けましょう。

3-3. 桁数を指定してゼロ埋めする方法

16進数文字列の桁数を揃えたい場合は、"X2""X8"のように桁数を指定します。

C#
int value = 15;

Console.WriteLine(value.ToString("X")); // F
Console.WriteLine(value.ToString("X2")); // 0F
Console.WriteLine(value.ToString("X4")); // 000F

byteを16進数で表す場合は、2桁に揃えるのが一般的です。

C#
byte b = 10;

Console.WriteLine(b.ToString("X2")); // 0A

intを8桁の16進数で表示したい場合は、"X8"を使います。

C#
int value = 255;

Console.WriteLine(value.ToString("X8")); // 000000FF

3-4. Convert.ToString(value, 16)を使う方法

Convert.ToString(value, 16)でも、数値を16進数文字列に変換できます。

C#
int value = 255;

string hex = Convert.ToString(value, 16);

Console.WriteLine(hex); // ff

Convert.ToString(value, 16)は小文字で出力されます。大文字にしたい場合はToUpperInvariant()を使います。

C#
int value = 255;

string hex = Convert.ToString(value, 16).ToUpperInvariant();

Console.WriteLine(hex); // FF

ただし、桁数指定やゼロ埋めを簡潔に書きたい場合は、ToString("X2")ToString("X8")のほうが使いやすいです。

3-5. string.Format・文字列補間で16進数を出力する方法

string.Formatや文字列補間でも、16進数形式を指定できます。

C#
int value = 255;

string s1 = string.Format("{0:X}", value);
string s2 = $"{value:X}";

Console.WriteLine(s1); // FF
Console.WriteLine(s2); // FF

桁数を指定することもできます。

C#
int value = 15;

Console.WriteLine($"{value:X2}"); // 0F
Console.WriteLine($"{value:X8}"); // 0000000F

ログ出力では、文字列補間を使うと読みやすくなります。

C#
int address = 4096;
int data = 255;

Console.WriteLine($"Address=0x{address:X4}, Data=0x{data:X2}");
// Address=0x1000, Data=0xFF

3-6. byteを2桁の16進数文字列に変換する方法

byteは0〜255の値なので、16進数では必ず2桁以内に収まります。実務では、1桁の場合もゼロ埋めして2桁にすることが多いです。

C#
byte b1 = 0;
byte b2 = 10;
byte b3 = 255;

Console.WriteLine(b1.ToString("X2")); // 00
Console.WriteLine(b2.ToString("X2")); // 0A
Console.WriteLine(b3.ToString("X2")); // FF

byte配列を1バイトずつ変換する場合も、"X2"が基本です。

C#
byte[] bytes = { 0x00, 0x0A, 0xFF };

foreach (byte b in bytes)
{
Console.Write(b.ToString("X2"));
}

// 000AFF

4. 16進数文字列を数値に変換する方法

4-1. Convert.ToInt32(hex, 16)で変換する

16進数文字列をintに変換する基本的な方法は、Convert.ToInt32(hex, 16)です。

C#
string hex = "FF";

int value = Convert.ToInt32(hex, 16);

Console.WriteLine(value); // 255

第2引数の16は、16進数として解釈するという意味です。

C#
Console.WriteLine(Convert.ToInt32("10", 16)); // 16
Console.WriteLine(Convert.ToInt32("A", 16)); // 10
Console.WriteLine(Convert.ToInt32("FF", 16)); // 255

4-2. int.ParseとNumberStyles.HexNumberを使う方法

int.Parseを使う場合は、NumberStyles.HexNumberを指定します。

C#
using System.Globalization;

string hex = "FF";

int value = int.Parse(hex, NumberStyles.HexNumber);

Console.WriteLine(value); // 255

NumberStyles.HexNumberを指定しないと、通常の10進数として解釈されるため、"FF"のような文字列は変換できません。

C#
// int.Parse("FF"); // FormatException

4-3. int.TryParseで安全に変換する方法

ユーザー入力や外部データを変換する場合は、例外を避けるためにint.TryParseを使うのがおすすめです。

C#
using System.Globalization;

string hex = "FF";

bool success = int.TryParse(
hex,
NumberStyles.HexNumber,
CultureInfo.InvariantCulture,
out int value);

if (success)
{
Console.WriteLine(value); // 255
}
else
{
Console.WriteLine("変換できませんでした");
}

TryParseは変換に失敗しても例外を投げず、falseを返します。入力値の妥当性が保証されない場合は、ParseよりもTryParseのほうが安全です。

4-4. 「0x」付き文字列を変換する際の注意点

C#の数値リテラルでは0xFFのように書けますが、int.ParseConvert.ToInt32で文字列を変換する場合は注意が必要です。

C#
string hex = "0xFF";

// int value = Convert.ToInt32(hex, 16); // FormatException

Convert.ToInt32(hex, 16)は、基本的に"FF"のような文字列を期待します。"0x"が付いている場合は、事前に取り除きます。

C#
string hex = "0xFF";

if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
hex = hex[2..];
}

int value = Convert.ToInt32(hex, 16);

Console.WriteLine(value); // 255

#FF0000のようなカラーコードを扱う場合も、先頭の#を取り除いてから変換します。

4-5. byte・short・long・uintなど型別の変換方法

変換先の型に応じて、対応するメソッドを使います。

C#
byte b = Convert.ToByte("FF", 16);
short s = Convert.ToInt16("7FFF", 16);
int i = Convert.ToInt32("7FFFFFFF", 16);
long l = Convert.ToInt64("7FFFFFFFFFFFFFFF", 16);

uint ui = Convert.ToUInt32("FFFFFFFF", 16);
ulong ul = Convert.ToUInt64("FFFFFFFFFFFFFFFF", 16);

Console.WriteLine(b); // 255
Console.WriteLine(ui); // 4294967295

Parseを使う場合も、型ごとのParseメソッドを使えます。

C#
using System.Globalization;

byte b = byte.Parse("FF", NumberStyles.HexNumber);
uint ui = uint.Parse("FFFFFFFF", NumberStyles.HexNumber);

ただし、変換先の型の範囲を超える文字列を渡すとOverflowExceptionが発生します。

C#
// byte b = Convert.ToByte("100", 16); // 256なのでOverflowException

4-6. 変換できない文字列を判定する方法

変換できるかどうかを判定するには、TryParseを使います。

C#
using System.Globalization;

static bool IsHexInt(string text)
{
return int.TryParse(
text,
NumberStyles.HexNumber,
CultureInfo.InvariantCulture,
out _);
}

Console.WriteLine(IsHexInt("FF")); // True
Console.WriteLine(IsHexInt("GG")); // False
Console.WriteLine(IsHexInt("0xFF")); // False

0x付きも許可したい場合は、事前に正規化します。

C#
using System.Globalization;

static bool IsHexIntAllowPrefix(string text)
{
if (string.IsNullOrWhiteSpace(text))
{
return false;
}

text = text.Trim();

if (text.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
text = text[2..];
}

return int.TryParse(
text,
NumberStyles.HexNumber,
CultureInfo.InvariantCulture,
out _);
}

5. バイト配列と16進数文字列を相互変換する方法

5-1. byte[]を16進数文字列に変換する基本パターン

byte[]を16進数文字列に変換する基本は、各byteを"X2"で変換して連結する方法です。

C#
byte[] bytes = { 0x00, 0x0A, 0xFF };

var sb = new System.Text.StringBuilder();

foreach (byte b in bytes)
{
sb.Append(b.ToString("X2"));
}

string hex = sb.ToString();

Console.WriteLine(hex); // 000AFF

1バイトは16進数2桁で表せるため、"X2"でゼロ埋めするのがポイントです。

5-2. BitConverter.ToStringを使う方法

BitConverter.ToStringを使うと、byte配列を簡単に16進数文字列へ変換できます。

C#
byte[] bytes = { 0x00, 0x0A, 0xFF };

string hex = BitConverter.ToString(bytes);

Console.WriteLine(hex); // 00-0A-FF

ただし、BitConverter.ToStringの結果にはハイフンが入ります。ログやデバッグでは見やすいですが、ハッシュ値やAPI送信用の文字列としてはハイフンなしにしたいこともあります。

5-3. ハイフンなしの連続した16進数文字列にする方法

BitConverter.ToStringの結果からハイフンを削除すれば、連続した16進数文字列にできます。

C#
byte[] bytes = { 0x00, 0x0A, 0xFF };

string hex = BitConverter.ToString(bytes).Replace("-", "");

Console.WriteLine(hex); // 000AFF

ただし、頻繁に大量データを処理する場合は、Replaceによる追加処理が気になることもあります。その場合は、Convert.ToHexStringStringBuilderを使う方法を検討しましょう。

5-4. Convert.ToHexStringを使う方法

.NET 5以降では、Convert.ToHexStringを使うとbyte配列を簡単にハイフンなしの16進数文字列へ変換できます。

C#
byte[] bytes = { 0x00, 0x0A, 0xFF };

string hex = Convert.ToHexString(bytes);

Console.WriteLine(hex); // 000AFF

Convert.ToHexStringは、大文字の16進数文字列を返します。現在の.NET環境で使える場合は、byte配列から16進数文字列への変換として非常に便利です。

5-5. 16進数文字列をbyte[]に戻す方法

16進数文字列をbyte配列に戻すには、2文字ずつ取り出してConvert.ToByteで変換します。

C#
string hex = "000AFF";

byte[] bytes = new byte[hex.Length / 2];

for (int i = 0; i < bytes.Length; i++)
{
string part = hex.Substring(i * 2, 2);
bytes[i] = Convert.ToByte(part, 16);
}

Console.WriteLine(bytes[0]); // 0
Console.WriteLine(bytes[1]); // 10
Console.WriteLine(bytes[2]); // 255

16進数2桁が1バイトに対応するため、文字列の長さは偶数である必要があります。

5-6. Convert.FromHexStringを使う方法

.NET 5以降では、Convert.FromHexStringを使うと、16進数文字列を簡単にbyte配列へ変換できます。

C#
string hex = "000AFF";

byte[] bytes = Convert.FromHexString(hex);

Console.WriteLine(bytes[0]); // 0
Console.WriteLine(bytes[1]); // 10
Console.WriteLine(bytes[2]); // 255

Convert.ToHexStringConvert.FromHexStringを組み合わせると、byte配列と16進数文字列の相互変換を簡潔に書けます。

C#
byte[] original = { 0xDE, 0xAD, 0xBE, 0xEF };

string hex = Convert.ToHexString(original);
byte[] restored = Convert.FromHexString(hex);

Console.WriteLine(hex); // DEADBEEF
Console.WriteLine(original.SequenceEqual(restored)); // True

5-7. 奇数桁や不正文字がある場合の対処法

16進数文字列をbyte配列に変換する場合、文字列の長さが奇数だと1バイトに分割できません。

C#
string hex = "ABC"; // 3桁なので不正

このような場合は、事前にチェックします。

C#
static bool IsValidHexByteString(string hex)
{
if (string.IsNullOrWhiteSpace(hex))
{
return false;
}

hex = hex.Trim();

if (hex.Length % 2 != 0)
{
return false;
}

foreach (char c in hex)
{
bool isHex =
('0' <= c && c <= '9') ||
('a' <= c && c <= 'f') ||
('A' <= c && c <= 'F');

if (!isHex)
{
return false;
}
}

return true;
}

Convert.FromHexStringを使う場合も、不正な文字列では例外が発生します。外部入力を扱う場合は、事前チェックまたは例外処理を入れると安全です。

6. C#の16進数変換でよくあるエラーと対策

6-1. FormatExceptionが発生する原因と解決方法

FormatExceptionは、文字列の形式が正しくないときに発生します。

C#
// Convert.ToInt32("GG", 16); // FormatException
// Convert.ToInt32("0xFF", 16); // FormatException

原因として多いのは次のケースです。

原因対策
GやZなど16進数でない文字が含まれる入力値をチェックする
0xが付いている事前に取り除く
#が付いているカラーコードなら先頭の#を取り除く
空白や改行が混ざるTrim()する
空文字事前に空チェックする

安全に処理するなら、TryParseを使います。

C#
using System.Globalization;

string input = "FF";

if (int.TryParse(input, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int value))
{
Console.WriteLine(value);
}
else
{
Console.WriteLine("16進数として変換できません");
}

6-2. OverflowExceptionが発生する原因と解決方法

OverflowExceptionは、変換先の型に収まらない値を変換しようとしたときに発生します。

C#
// byte b = Convert.ToByte("100", 16); // 256なのでOverflowException

byteの最大値は255、つまり16進数ではFFです。100は16進数で256を意味するため、byteには入りません。

対策としては、より大きな型へ変換するか、事前に範囲チェックを行います。

C#
int value = Convert.ToInt32("100", 16);

if (value <= byte.MaxValue)
{
byte b = (byte)value;
Console.WriteLine(b);
}
else
{
Console.WriteLine("byteの範囲を超えています");
}

6-3. 「0x」が付いているとParseできない場合の対処

0xFFという表記はC#のコード上では有効ですが、文字列としてParseする場合はそのままでは失敗することがあります。

C#
using System.Globalization;

string input = "0xFF";

// int.Parse(input, NumberStyles.HexNumber); // FormatException

この場合は、0xを削除してから変換します。

C#
using System.Globalization;

string input = "0xFF";
string hex = input.Trim();

if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
hex = hex[2..];
}

int value = int.Parse(hex, NumberStyles.HexNumber);

Console.WriteLine(value); // 255

0XFFのように大文字のXが使われることもあるため、StringComparison.OrdinalIgnoreCaseで判定すると便利です。

6-4. マイナス値を16進数に変換したときの注意点

負の数をToString("X")で16進数に変換すると、2の補数表現に基づいた表示になります。

C#
int value = -1;

Console.WriteLine(value.ToString("X")); // FFFFFFFF

これは、int型の-1がビット表現としては全ビット1になるためです。

C#
short s = -1;
int i = -1;
long l = -1;

Console.WriteLine(s.ToString("X")); // FFFF
Console.WriteLine(i.ToString("X")); // FFFFFFFF
Console.WriteLine(l.ToString("X")); // FFFFFFFFFFFFFFFF

「符号付きの値として-1のように表示したい」のか、「ビットパターンとしてFFFFFFFFを見たい」のかによって、扱い方を分ける必要があります。

6-5. 大文字・小文字・空白・改行が混ざる場合の処理

16進数文字列では、大文字と小文字はどちらでも変換できます。

C#
Console.WriteLine(Convert.ToInt32("ff", 16)); // 255
Console.WriteLine(Convert.ToInt32("FF", 16)); // 255

ただし、前後に空白や改行がある場合は、Trim()しておくと安全です。

C#
string input = "  FF\r\n ";

int value = Convert.ToInt32(input.Trim(), 16);

Console.WriteLine(value); // 255

バイト列のように途中に空白が入っている場合は、必要に応じて削除します。

C#
string input = "DE AD BE EF";

string hex = input.Replace(" ", "");

byte[] bytes = Convert.FromHexString(hex);

Console.WriteLine(Convert.ToHexString(bytes)); // DEADBEEF

6-6. nullや空文字を変換するときの安全な書き方

nullや空文字をそのまま変換しようとすると、例外や意図しない結果につながります。

C#
string? input = null;

if (string.IsNullOrWhiteSpace(input))
{
Console.WriteLine("入力が空です");
}
else
{
int value = Convert.ToInt32(input, 16);
Console.WriteLine(value);
}

実務では、次のように正規化処理とTryParseを組み合わせると安全です。

C#
using System.Globalization;

static bool TryParseHexInt(string? input, out int value)
{
value = 0;

if (string.IsNullOrWhiteSpace(input))
{
return false;
}

string hex = input.Trim();

if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
hex = hex[2..];
}

return int.TryParse(
hex,
NumberStyles.HexNumber,
CultureInfo.InvariantCulture,
out value);
}

7. 用途別に見るC#の16進数変換サンプル

7-1. カラーコードを数値やRGBに変換する

カラーコード#FF3366をRGBに分解する例です。

C#
string colorCode = "#FF3366";

string hex = colorCode.TrimStart('#');

int r = Convert.ToInt32(hex.Substring(0, 2), 16);
int g = Convert.ToInt32(hex.Substring(2, 2), 16);
int b = Convert.ToInt32(hex.Substring(4, 2), 16);

Console.WriteLine($"R={r}, G={g}, B={b}");
// R=255, G=51, B=102

RGB値からカラーコードを作る場合は、"X2"で2桁に揃えます。

C#
int r = 255;
int g = 51;
int b = 102;

string colorCode = $"#{r:X2}{g:X2}{b:X2}";

Console.WriteLine(colorCode); // #FF3366

7-2. 文字列の各文字を16進数コードで表示する

文字列の各文字をUnicodeコードで表示する例です。

C#
string text = "ABCあ";

foreach (char c in text)
{
Console.WriteLine($"{c} = U+{(int)c:X4}");
}

出力例です。

A = U+0041
B = U+0042
C = U+0043
あ = U+3042

サロゲートペアを含む文字を正しくコードポイントとして扱いたい場合は、Runeを使う方法もあります。

C#
using System.Text;

string text = "😀";

foreach (var rune in text.EnumerateRunes())
{
Console.WriteLine($"U+{rune.Value:X}");
}

7-3. ファイルや通信データを16進数ダンプする

ファイルや通信データを確認するときは、byte配列を16進数で出力すると便利です。

C#
byte[] data = { 0x48, 0x65, 0x6C, 0x6C, 0x6F };

for (int i = 0; i < data.Length; i++)
{
Console.Write($"{data[i]:X2} ");
}

// 48 65 6C 6C 6F

アドレス風に表示する簡単な16進数ダンプ例です。

C#
byte[] data = Enumerable.Range(0, 64).Select(i => (byte)i).ToArray();

for (int i = 0; i < data.Length; i += 16)
{
Console.Write($"{i:X4}: ");

for (int j = 0; j < 16 && i + j < data.Length; j++)
{
Console.Write($"{data[i + j]:X2} ");
}

Console.WriteLine();
}

7-4. ハッシュ値やバイナリデータを16進数で表示する

ハッシュ値はbyte配列で得られることが多いため、16進数文字列に変換して表示します。

C#
using System.Security.Cryptography;
using System.Text;

string text = "hello";

byte[] bytes = Encoding.UTF8.GetBytes(text);
byte[] hash = SHA256.HashData(bytes);

string hex = Convert.ToHexString(hash);

Console.WriteLine(hex);

小文字で表示したい場合は、ToLowerInvariant()を使います。

C#
string hexLower = Convert.ToHexString(hash).ToLowerInvariant();

Console.WriteLine(hexLower);

7-5. Unicodeコードポイントを16進数で扱う

Unicodeコードポイントは、U+3042のように16進数で表されます。

C#
int codePoint = 0x3042;

string text = char.ConvertFromUtf32(codePoint);

Console.WriteLine(text); // あ

逆に、文字からコードポイントを取得する場合は次のように書けます。

C#
string text = "あ";

int codePoint = char.ConvertToUtf32(text, 0);

Console.WriteLine($"U+{codePoint:X4}"); // U+3042

絵文字のように1つの文字が複数のcharで構成される場合があるため、Unicodeを本格的に扱う場合はchar単位ではなくコードポイント単位で考えることが重要です。

7-6. デバッグログに16進数を出力する

デバッグログでは、10進数と16進数を併記すると原因調査がしやすくなります。

C#
int status = 255;

Console.WriteLine($"Status={status} (0x{status:X2})");
// Status=255 (0xFF)

フラグやビットマスクを扱う場合は、16進数で表示するとビットパターンを把握しやすくなります。

C#
int flags = 0b1010_0101;

Console.WriteLine($"Flags=0x{flags:X2}");
// Flags=0xA5

8. C#の16進数変換で使うメソッド比較

8-1. ToString("X")とConvert.ToStringの違い

数値を16進数文字列にする方法として、ToString("X")Convert.ToString(value, 16)があります。

方法出力例特徴
value.ToString("X")FF大文字、桁数指定が簡単
value.ToString("x")ff小文字、桁数指定が簡単
Convert.ToString(value, 16)ff基数を指定できる

実務では、桁数を揃えたいことが多いため、ToString("X2")ToString("X8")がよく使われます。

C#
int value = 15;

Console.WriteLine(value.ToString("X2")); // 0F
Console.WriteLine(Convert.ToString(value, 16)); // f

8-2. ParseとTryParseの使い分け

Parseは変換できないと例外を投げます。入力が必ず正しいと分かっている場合に向いています。

C#
using System.Globalization;

int value = int.Parse("FF", NumberStyles.HexNumber);

TryParseは変換に失敗しても例外を投げません。ユーザー入力、外部ファイル、APIレスポンスなどを扱う場合に向いています。

C#
using System.Globalization;

if (int.TryParse("FF", NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int value))
{
Console.WriteLine(value);
}

基本的には、外部入力にはTryParse、固定値やテストコードにはParseという使い分けがおすすめです。

8-3. BitConverterとConvert.ToHexStringの違い

byte配列を16進数文字列にする方法として、BitConverter.ToStringConvert.ToHexStringがあります。

方法出力例特徴
BitConverter.ToString(bytes)DE-AD-BE-EFハイフン付き
Convert.ToHexString(bytes)DEADBEEFハイフンなし
自作ループDEADBEEF古い環境でも使える

Convert.ToHexStringが使える環境では、ハイフンなしの16進数文字列を簡単に作れるため便利です。

C#
byte[] bytes = { 0xDE, 0xAD, 0xBE, 0xEF };

Console.WriteLine(BitConverter.ToString(bytes)); // DE-AD-BE-EF
Console.WriteLine(Convert.ToHexString(bytes)); // DEADBEEF

8-4. Convert.FromHexStringと自作変換処理の違い

16進数文字列をbyte配列に戻す場合、.NET 5以降ならConvert.FromHexStringが簡潔です。

C#
byte[] bytes = Convert.FromHexString("DEADBEEF");

古い環境や、スペース・ハイフン・0xなどを許容したい場合は、自作で正規化してから変換します。

C#
string input = "DE-AD-BE-EF";

string hex = input.Replace("-", "");

byte[] bytes = Convert.FromHexString(hex);

自作処理では柔軟な入力に対応できますが、バグを生みやすい点に注意が必要です。使える環境であれば、標準メソッドを優先するのがおすすめです。

8-5. 処理速度・可読性・対応バージョンで選ぶ方法

16進数変換の方法は、可読性、処理速度、対応バージョンで選ぶとよいです。

目的おすすめ
数値を16進数文字列にするToString("X")
桁数を揃えるToString("X2")ToString("X8")
16進数文字列を数値にするTryParseまたはConvert.ToInt32
byte[]を16進数文字列にするConvert.ToHexString
16進数文字列をbyte[]にするConvert.FromHexString
古い.NETでbyte[]を変換するBitConverter.ToStringまたは自作
外部入力を安全に扱う正規化 + TryParse

最新の.NETを使えるなら、byte配列の変換にはConvert.ToHexStringConvert.FromHexStringを使うと、コードが短く読みやすくなります。

9. 実務で使いやすい16進数変換の実装例

9-1. 数値を固定桁の16進数に変換する関数

数値を固定桁の16進数文字列にする関数です。

C#
static string ToHex(int value, int digits)
{
return value.ToString($"X{digits}");
}

Console.WriteLine(ToHex(15, 2)); // 0F
Console.WriteLine(ToHex(255, 4)); // 00FF
Console.WriteLine(ToHex(4096, 8)); // 00001000

0x付きで出力したい場合は、次のようにします。

C#
static string ToHexWithPrefix(int value, int digits)
{
return $"0x{value.ToString($"X{digits}")}";
}

Console.WriteLine(ToHexWithPrefix(255, 2)); // 0xFF

9-2. 16進数文字列を安全にintへ変換する関数

0x付き、前後空白ありの入力にも対応した安全な変換関数です。

C#
using System.Globalization;

static bool TryHexToInt(string? input, out int value)
{
value = 0;

if (string.IsNullOrWhiteSpace(input))
{
return false;
}

string hex = input.Trim();

if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
hex = hex[2..];
}

return int.TryParse(
hex,
NumberStyles.HexNumber,
CultureInfo.InvariantCulture,
out value);
}

if (TryHexToInt("0xFF", out int result))
{
Console.WriteLine(result); // 255
}

ユーザー入力や設定ファイルの値を扱う場合は、このように例外を出さない実装にしておくと安全です。

9-3. byte[]をハイフンなし16進数文字列に変換する関数

.NET 5以降なら、次の関数で十分です。

C#
static string BytesToHex(byte[] bytes)
{
return Convert.ToHexString(bytes);
}

byte[] bytes = { 0xDE, 0xAD, 0xBE, 0xEF };

Console.WriteLine(BytesToHex(bytes)); // DEADBEEF

小文字にしたい場合は、ToLowerInvariant()を使います。

C#
static string BytesToHexLower(byte[] bytes)
{
return Convert.ToHexString(bytes).ToLowerInvariant();
}

Console.WriteLine(BytesToHexLower(bytes)); // deadbeef

古い.NET環境では、StringBuilderで実装できます。

C#
static string BytesToHexLegacy(byte[] bytes)
{
var sb = new System.Text.StringBuilder(bytes.Length * 2);

foreach (byte b in bytes)
{
sb.Append(b.ToString("X2"));
}

return sb.ToString();
}

9-4. 16進数文字列をbyte[]へ変換する関数

.NET 5以降なら、Convert.FromHexStringを使うのが簡単です。

C#
static byte[] HexToBytes(string hex)
{
return Convert.FromHexString(hex);
}

byte[] bytes = HexToBytes("DEADBEEF");

Console.WriteLine(bytes.Length); // 4

入力に空白やハイフンが混ざる可能性がある場合は、事前に取り除きます。

C#
static byte[] HexToBytesFlexible(string input)
{
if (input is null)
{
throw new ArgumentNullException(nameof(input));
}

string hex = input
.Trim()
.Replace(" ", "")
.Replace("-", "");

if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
hex = hex[2..];
}

return Convert.FromHexString(hex);
}

byte[] bytes = HexToBytesFlexible("DE-AD-BE-EF");

Console.WriteLine(Convert.ToHexString(bytes)); // DEADBEEF

9-5. 不正な入力を考慮した汎用的な変換処理

例外を投げずに、16進数文字列をbyte配列へ変換できるか判定する関数です。

C#
static bool TryHexToBytes(string? input, out byte[] bytes)
{
bytes = Array.Empty<byte>();

if (string.IsNullOrWhiteSpace(input))
{
return false;
}

string hex = input
.Trim()
.Replace(" ", "")
.Replace("-", "");

if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
hex = hex[2..];
}

if (hex.Length == 0 || hex.Length % 2 != 0)
{
return false;
}

foreach (char c in hex)
{
bool isHex =
('0' <= c && c <= '9') ||
('a' <= c && c <= 'f') ||
('A' <= c && c <= 'F');

if (!isHex)
{
return false;
}
}

try
{
bytes = Convert.FromHexString(hex);
return true;
}
catch
{
bytes = Array.Empty<byte>();
return false;
}
}

使用例です。

C#
if (TryHexToBytes("DE AD BE EF", out byte[] bytes))
{
Console.WriteLine(Convert.ToHexString(bytes)); // DEADBEEF
}
else
{
Console.WriteLine("不正な16進数文字列です");
}

外部入力では、空文字、奇数桁、不正文字、0x、空白、ハイフンなどを考慮しておくと、実務でトラブルを減らせます。

10. C#の16進数に関するよくある質問

10-1. C#で16進数の先頭に0xは必要?

C#のコード内で16進数リテラルを書く場合は、先頭に0xが必要です。

C#
int value = 0xFF;

一方、文字列として16進数を扱う場合は、必ずしも0xは必要ありません。

C#
string hex = "FF";
int value = Convert.ToInt32(hex, 16);

むしろ、Convert.ToInt32(hex, 16)int.Parseでは、0xが付いていると変換できないことがあるため注意が必要です。

10-2. 16進数文字列に0xが付いているときはどう変換する?

0xを取り除いてから変換します。

C#
string input = "0xFF";

string hex = input.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
? input[2..]
: input;

int value = Convert.ToInt32(hex, 16);

Console.WriteLine(value); // 255

前後に空白がある可能性がある場合は、先にTrim()します。

C#
string input = " 0xFF ";

string hex = input.Trim();

if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
hex = hex[2..];
}

int value = Convert.ToInt32(hex, 16);

10-3. 16進数を10進数に変換するには?

Convert.ToInt32(hex, 16)を使います。

C#
string hex = "FF";

int value = Convert.ToInt32(hex, 16);

Console.WriteLine(value); // 255

安全に変換したい場合は、int.TryParseを使います。

C#
using System.Globalization;

string hex = "FF";

if (int.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int value))
{
Console.WriteLine(value); // 255
}

10-4. 10進数を16進数に変換するには?

数値に対してToString("X")を使います。

C#
int value = 255;

string hex = value.ToString("X");

Console.WriteLine(hex); // FF

小文字にしたい場合は"x"を使います。

C#
Console.WriteLine(value.ToString("x")); // ff

10-5. byte配列を16進数文字列にする最短の方法は?

.NET 5以降なら、Convert.ToHexStringが最短で読みやすい方法です。

C#
byte[] bytes = { 0xDE, 0xAD, 0xBE, 0xEF };

string hex = Convert.ToHexString(bytes);

Console.WriteLine(hex); // DEADBEEF

古い環境では、BitConverter.ToStringを使う方法があります。

C#
string hex = BitConverter.ToString(bytes).Replace("-", "");

Console.WriteLine(hex); // DEADBEEF

10-6. C#で16進数の桁数を揃えるには?

ToString("X2")ToString("X8")のように、Xの後ろに桁数を指定します。

C#
int value = 15;

Console.WriteLine(value.ToString("X2")); // 0F
Console.WriteLine(value.ToString("X4")); // 000F
Console.WriteLine(value.ToString("X8")); // 0000000F

byteは2桁、shortは4桁、intは8桁、longは16桁で表示すると、ビット幅に合わせやすくなります。

C#
byte b = 255;
short s = 255;
int i = 255;
long l = 255;

Console.WriteLine(b.ToString("X2")); // FF
Console.WriteLine(s.ToString("X4")); // 00FF
Console.WriteLine(i.ToString("X8")); // 000000FF
Console.WriteLine(l.ToString("X16")); // 00000000000000FF

10-7. 負の数を16進数にするとどう表示される?

負の数をToString("X")で16進数にすると、型のビット幅に応じた2の補数表現で表示されます。

C#
int value = -1;

Console.WriteLine(value.ToString("X")); // FFFFFFFF

shortintlongでは表示される桁数が異なります。

C#
short s = -1;
int i = -1;
long l = -1;

Console.WriteLine(s.ToString("X")); // FFFF
Console.WriteLine(i.ToString("X")); // FFFFFFFF
Console.WriteLine(l.ToString("X")); // FFFFFFFFFFFFFFFF

負の数を扱う場合は、符号付きの値として見たいのか、ビットパターンとして見たいのかを明確にしておくことが重要です。

まとめ

C#で16進数を扱う基本は、数値リテラルでは0x、数値から16進数文字列への変換ではToString("X")、16進数文字列から数値への変換ではConvert.ToInt32(hex, 16)int.TryParseを使うことです。

byte配列を16進数文字列に変換する場合は、.NET 5以降ならConvert.ToHexStringが便利です。逆に、16進数文字列をbyte配列へ戻す場合はConvert.FromHexStringを使うと簡潔に書けます。

一方で、実務では0x付き文字列、空白や改行、不正文字、奇数桁、型の範囲超過、負の数の表示などに注意が必要です。外部入力を扱う場合は、Trim()やプレフィックス除去で入力を正規化し、TryParseや事前チェックを使って安全に変換しましょう。

C#の16進数変換は、カラーコード、文字コード、バイナリデータ、ハッシュ値、通信ログ、デバッグ出力など幅広い場面で役立ちます。基本的な変換方法とエラー対策を押さえておけば、数値やバイト列を扱うコードをより読みやすく、安定して実装できます。