.NET 文字列を指定したバイト数以内で切り出す


.NETでは、String.Substringや範囲演算子を使用して、文字列の先頭から指定した長さを切り出すことができます。

string result = value.Substring(0, length);

string result = value[..length];

ただし、.NETのstringはUTF-16で管理されています。LengthやSubstringなどで使用するインデックスは、見た目の文字数やUnicodeのコードポイント数ではなく、UTF-16のコードユニット(char)単位です。そのため、UTF-8などのエンコーディングに変換した場合のバイト数を基準に切り出したい場合は、Substringなどをそのまま使用することはできません。

また、UTF-8のような可変長エンコーディングでは、1つの文字(Unicodeコードポイント)を複数バイトで表現することがあります。そのため、エンコード後のバイト列を指定した位置で単純に切ると、1文字分のバイト列が途中で分割されてしまう可能性があります。

このような場合は、Encoder.Convertが使用できます。

Span<byte> buffer = stackalloc byte[length];

Encoding encoding = Encoding.UTF8;
Encoder encoder = encoding.GetEncoder();

encoder.Convert(
	value.AsSpan(),
	buffer,
	flush: true,
	out int charsUsed,
	out int bytesUsed,
	out _);

ReadOnlySpan<byte> bytes = buffer[..bytesUsed];
string result = value[..charsUsed];

Encoder.Convertは、出力先のbufferに収まる範囲で入力文字列をエンコードします。このとき、1文字分のバイト列が途中で切れることはありません。

charsUsedには、エンコード時に消費された入力文字列のUTF-16のコードユニット数が格納されます。そのため、charsUsedを範囲演算子のインデックスとして使用することで、指定したバイト数以内に収まる部分を元の文字列から切り出せます。

また、bytesUsedには、実際に出力されたバイト数が格納されます。バイト列が必要な場合は、bufferの先頭bytesUsedバイト分から取得できます。

ただし、bufferが小さくて1つもエンコードできない場合は、ArgumentExceptionをスローするので注意が必要です。

このブログの人気の投稿

googleドライブの同期は、フォルダ選択ができました。

なかなか消えない亀アイコン

技術メモ「503 Service Unavailable」