byte型配列との相互変換

データ型の相互変換の項ではint型やstring型などを相互変換する方法を解説しましたが、プログラミングでは時に様々な値をbyte型の配列で扱う場合があります。
ここではデータ型とbyte型配列(バイナリ)とを相互変換する方法を説明します。

int型→string型などのデータ型の変換はデータ型の相互変換を参照してください。

BitConverterクラス

BitConverterクラスはbyte型配列との相互変換メソッドを提供するクラスです。
(System名前空間)
メソッドはすべて静的メソッドです。

BitConverter.GetBytes

int型などをbyte型配列に変換するにはBitConverter.GetBytesメソッドを使用します。


byte[] byteInts = BitConverter.GetBytes(12345);     //int型
byte[] byteDoubles = BitConverter.GetBytes(1.2345); //double型

//16進数で表示
foreach (var x in byteInts)
    Console.Write("{0:X2} ", x);
Console.WriteLine();

foreach (var x in byteDoubles)
    Console.Write("{0:X2} ", x);
39 30 00 00
8D 97 6E 12 83 C0 F3 3F

BitConverter.GetBytesメソッドは基本的な値型(プリミティブ型)を全てサポートしています。

コンピューターは、あらゆる値を二進数で保存しています。
これをそのまま二進数で表示すると0と1の膨大な羅列が表示されることになりますが、それでは見づらいので16進数で表示することが多いです。

16進数は0~Fの16種の文字で16通りの値を表します。
(アルファベットの大文字/小文字はどちらでも構いません)
16通りの値は4bitのデータ量になりますから(2の4乗)、1バイト(8bit)の情報量は16進数では2桁で表すことができます。
メモリ上には1バイト単位でデータが保存されるので、2桁ずつ表示を区切ることが多いです。

int型は4バイトなので2×4=8桁、double型は8バイトなので2×8=16桁で表されます。

エンディアン

例えばint型の数値は4バイトのサイズですが、メモリ上では1バイト単位で管理されています。
この4バイトのデータをどのような順序で並べるかの決まりをエンディアン(バイトオーダー)といいます。


Action<byte[]> showBytes = (byte[] bytes) => { 
    foreach (byte b in bytes)
        Console.Write("{0:X2} ", b);
    Console.WriteLine();
};

showBytes(BitConverter.GetBytes(1));
showBytes(BitConverter.GetBytes(15));
showBytes(BitConverter.GetBytes(16));
showBytes(BitConverter.GetBytes(31));
showBytes(BitConverter.GetBytes(255));
showBytes(BitConverter.GetBytes(256));
01 00 00 00
0F 00 00 00
10 00 00 00
1F 00 00 00
FF 00 00 00
00 01 00 00

数値の「1」をあえて8桁で表示すると「00000001」です。
しかし上のコードの実行結果を見ると、桁の低い方から順に並べられているのが分かります。
(バイト単位)
このような並べ方をリトルエンディアンといい、Windows環境は基本的にリトルエンディアンとなっています。

int型の「1」を「00 00 00 01」と表現する方法をビッグエンディアンといいます。
その他ミドルエンディアンというものも存在します。

このようなバイトの並びは一つのプログラムで完結するような場合には気にする必要はありません。
例えば他の環境で作成されたファイルを読み込む場合や、他の環境とネットワークを通じて通信する場合に問題となる場合があります。

.NETではエンディアンの変換を行う機能は標準では提供されていないので、必要ならば自作するか標準以外のライブラリを使用する必要があります。

BitConverter.To○○

byte型配列を元のデータ型に戻すには以下のメソッドを使用します。

BitConverter.ToBoolern bool型
BitConverter.ToChar char型
BitConverter.ToInt16
BitConverter.ToUint16
short型
ushort型
BitConverter.ToInt32
BitConverter.ToUInt32
int型
uint型
BitConverter.ToInt64
BitConverter.ToUInt64
long型
ulong型
BitConverter.ToSingle float型
BitConverter.ToDouble double型

byte[] byteInts = BitConverter.GetBytes(12345);
byte[] byteDoubles = BitConverter.GetBytes(1.2345);

int num = BitConverter.ToInt32(byteInts, 0);
double real = BitConverter.ToDouble(byteDoubles, 0);

Console.WriteLine(num);
Console.WriteLine(real);
12345
1.2345

第二引数はbyte型配列の読み取り開始位置を指定します。
これは複数のデータが含まれるbyte型配列から特定の位置のデータを取得します。


byte[] byteInts = BitConverter.GetBytes(12345);
byte[] byteDoubles = BitConverter.GetBytes(1.2345);

//二つの配列を結合
byte[] bytes = byteInts.Concat(byteDoubles).ToArray();

int num = BitConverter.ToInt32(bytes, 0);
double real = BitConverter.ToDouble(bytes, 4);
12345
1.2345

int型なら4バイト分、double型なら8バイト分の長さが指定位置から取得されます。
指定のデータ型のサイズ以上の長さがないとエラー(例外)になります。

BitConverter.ToString

BitConverter.ToStringメソッドは、byte型配列の各要素を16進数表記にし、ハイフンで区切った文字列に変換します。
使い方はToInt32メソッドなどと同じです。


byte[] byteInts = BitConverter.GetBytes(12345);
byte[] byteDoubles = BitConverter.GetBytes(1.2345);

string strInts = BitConverter.ToString(byteInts, 0);
string strDoubles = BitConverter.ToString(byteDoubles, 0);

Console.WriteLine(strInts);
Console.WriteLine(strDoubles);
39-30-00-00
8D-97-6E-12-83-C0-F3-3F

文字列とbyte型配列の相互変換は次に説明するEncodingクラスを使用します。

Encodingクラス

文字列をバイト型配列に変換するにはEncodingクラスを使用します。
(System.Text名前空間)

文字列→バイト配列

Encodingクラスにはいくつかの文字コードがあらかじめ用意されているので、これらのGetBytesメソッドで文字列をバイト型配列に変換します。


byte[] ASCII = Encoding.ASCII.GetBytes("あいうえお");
byte[] UTF7  = Encoding.UTF7.GetBytes("あいうえお");
byte[] UTF8  = Encoding.UTF8.GetBytes("あいうえお");
byte[] UTF16 = Encoding.Unicode.GetBytes("あいうえお");
byte[] UTF32 = Encoding.UTF32.GetBytes("あいうえお");

ASCIIは日本語を扱うことはできないので、上記のコード1行目は変換に失敗します。

「Encoding.Unicode」はUTF-16LE(リトルエンディアン)を意味します。

これら以外の文字コードを使用する場合はEncoding.GetEncodingメソッドを使用します。


byte[] SJIS1 = Encoding.GetEncoding("Shift_JIS").GetBytes("あいうえお");
byte[] SJIS2 = Encoding.GetEncoding(932).GetBytes("あいうえお");

引数には文字コードを表す文字列か数値を指定します。
「932」はShift_JISを表す番号で、上記の例はどちらもShift_JISで変換します。
引数に指定できる文字列や数値は以下を参照してください。
System.Text.Encoding クラス - .NET | Microsoft Learn

なお、GetEncodingメソッドはEncodingクラスのインスタンスを返します。
他でも同じ文字コードの設定を使用する場合は変数に保存しておくと使いまわしができます。
ちなみにあらかじめ用意されている文字コード(Encoding.UTF8など)は読み取り専用プロパティで、これもEncodingクラスのインスタンスを返します。


Encoding encSJIS = Encoding.GetEncoding("Shift_JIS");
byte[] byte1 = encSJIS.GetBytes("あいうえお");
byte[] byte2 = encSJIS.GetBytes("かきくけこ");

Encoding encUTF8 = Encoding.UTF8;

.NET Core環境(.NET5以降も含む)では、標準ではASCIIとUnicode系エンコーディングのみが使用できます。
Shift_JISなども使用可能にするにはEncoding.RegisterProviderメソッドを以下の形式で事前に実行する必要があります。


//.NET Coreでは必要
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

byte[] SJIS1 = Encoding.GetEncoding("Shift_JIS").GetBytes("あいうえお");
byte[] SJIS2 = Encoding.GetEncoding(932).GetBytes("あいうえお");

バイト配列→文字列

バイト配列を文字列に変換するにはGetStringメソッドを使用します。


byte[] bytesUTF8 = Encoding.UTF8.GetBytes("あいうえお");
byte[] bytesUTF16 = Encoding.Unicode.GetBytes("あいうえお");
byte[] bytesSJIS = Encoding.GetEncoding("Shift_JIS").GetBytes("あいうえお");

string utf8 = Encoding.UTF8.GetString(bytesUTF8);
string utf16 = Encoding.Unicode.GetString(bytesUTF16);
string sjis = Encoding.GetEncoding("Shift_JIS").GetString(bytesSJIS);

Console.WriteLine(utf8);
Console.WriteLine(utf16);
Console.WriteLine(sjis);
あいうえお
あいうえお
あいうえお

バイト配列に変換した時の文字コードと複合時の文字コードが一致しないと文字化けが発生します。

第二引数に変換開始位置、第三引数に変換するバイト数を指定することでバイト配列の特定の位置の変換ができます。


//配列の2番目から6バイト分を変換
//UTF16は2バイトで1文字
string str = Encoding.Unicode.GetString(bytesUTF16, 2, 6);
Console.WriteLine(str);
いうえ

BOMの取得

Unicode文字コードは、「UTF-8」や「UTF-16」などの複数の符号化方式が存在します。
その種類を識別するために、データの先頭にBOMという情報を付加することができます。
BOMはバイトオーダーマーク(バイト順マーク、Byte Order Mark)の略で、それぞれの種類を表す数バイトのデータです。

Encodingオブジェクト(インスタンス)が使用するBOMはGetPreambleメソッドで取得することができます。


byte[] bomUTF8 = Encoding.UTF8.GetPreamble();
byte[] bomUTF16 = Encoding.Unicode.GetPreamble();
byte[] bomUTF32 = Encoding.UTF32.GetPreamble();
byte[] bomSiftJIS = Encoding.GetEncoding("Shift_JIS").GetPreamble();

Console.WriteLine(BitConverter.ToString(bomUTF8));
Console.WriteLine(BitConverter.ToString(bomUTF16));
Console.WriteLine(BitConverter.ToString(bomUTF32));
Console.WriteLine(BitConverter.ToString(bomSiftJIS));
EF-BB-BF
FF-FE
FF-FE-00-00

戻り値はBOM情報が格納されたbyte型の配列です。
BOMは現在のところUnicodeだけの規格なので、Shift_JISなどの文字コードでは空のbyte型配列が返されます。