.NET での文字エンコードCharacter encoding in .NET

この記事では、.NET で使用される文字エンコード システムの概要について説明します。This article provides an introduction to character encoding systems that are used by .NET. この記事では、StringCharRune、および StringInfo 型が Unicode、UTF-16、および UTF-8 でどのように動作するかについて説明します。The article explains how the String, Char, Rune, and StringInfo types work with Unicode, UTF-16, and UTF-8.

ここでは、"文字" という用語が "読者が 1 つの表示要素として認識するもの" という全般的な意味で使用されます。The term character is used here in the general sense of what a reader perceives as a single display element. 一般的な例としては、文字 "a"、記号 "@"、絵文字 "🐂" などがあります。Common examples are the letter "a", the symbol "@", and the emoji "🐂". 場合によっては、1 つの文字に見えるものが、書記素クラスターに関するセクションで説明しているように、実際には複数の独立した表示要素で構成されていることがあります。Sometimes what looks like one character is actually composed of multiple independent display elements, as the section on grapheme clusters explains.

string 型と char 型The string and char types

string クラスのインスタンスは、何らかのテキストを表します。An instance of the string class represents some text. string は、論理的には 16 ビット値のシーケンスであり、そのそれぞれが char 構造体のインスタンスです。A string is logically a sequence of 16-bit values, each of which is an instance of the char struct. string.Length プロパティでは、string インスタンスに含まれる char インスタンスの数が返されます。The string.Length property returns the number of char instances in the string instance.

次のサンプル関数では、string に含まれるすべての char インスタンスの 16 進表記の値が出力されます。The following sample function prints out the values in hexadecimal notation of all the char instances in a string:

void PrintChars(string s)
{
    Console.WriteLine($"\"{s}\".Length = {s.Length}");
    for (int i = 0; i < s.Length; i++)
    {
        Console.WriteLine($"s[{i}] = '{s[i]}' ('\\u{(int)s[i]:x4}')");
    }
    Console.WriteLine();
}

この関数に string "Hello" を渡すと、次の出力が得られます。Pass the string "Hello" to this function, and you get the following output:

PrintChars("Hello");
"Hello".Length = 5
s[0] = 'H' ('\u0048')
s[1] = 'e' ('\u0065')
s[2] = 'l' ('\u006c')
s[3] = 'l' ('\u006c')
s[4] = 'o' ('\u006f')

各文字は、1 つの char 値で表されます。Each character is represented by a single char value. このパターンは、世界のほとんどの言語に当てはまります。That pattern holds true for most of the world's languages. たとえば、nǐ hǎo のように発音し "こんにちは" を意味する中国語の 2 文字に対する出力を次に示します。For example, here's the output for two Chinese characters that sound like nǐ hǎo and mean Hello:

PrintChars("你好");
"你好".Length = 2
s[0] = '你' ('\u4f60')
s[1] = '好' ('\u597d')

ただし、一部の言語および一部の記号と絵文字については、1 つの文字を表すために 2 つの char インスタンスが必要です。However, for some languages and for some symbols and emoji, it takes two char instances to represent a single character. たとえば、オセージ語で "オセージ" を表す単語に含まれる文字と char インスタンスを比較してください。For example, compare the characters and char instances in the word that means Osage in the Osage language:

PrintChars("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟");
"𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟".Length = 17
s[0] = '�' ('\ud801')
s[1] = '�' ('\udccf')
s[2] = '�' ('\ud801')
s[3] = '�' ('\udcd8')
s[4] = '�' ('\ud801')
s[5] = '�' ('\udcfb')
s[6] = '�' ('\ud801')
s[7] = '�' ('\udcd8')
s[8] = '�' ('\ud801')
s[9] = '�' ('\udcfb')
s[10] = '�' ('\ud801')
s[11] = '�' ('\udcdf')
s[12] = ' ' ('\u0020')
s[13] = '�' ('\ud801')
s[14] = '�' ('\udcbb')
s[15] = '�' ('\ud801')
s[16] = '�' ('\udcdf')

前の例では、空白を除く各文字が 2 つの char インスタンスによって表されています。In the preceding example, each character except the space is represented by two char instances.

また、雄牛の絵文字を表す次の例で示すように、1 つの Unicode 絵文字も 2 つの char によって表されます。A single Unicode emoji is also represented by two chars, as seen in the following example showing an ox emoji:

"🐂".Length = 2
s[0] = '�' ('\ud83d')
s[1] = '�' ('\udc02')

これらの例では、char インスタンスの数を示す string.Length の値が、必ずしも表示される文字数を示していないことがわかります。These examples show that the value of string.Length, which indicates the number of char instances, doesn't necessarily indicate the number of displayed characters. 1 つの char インスタンスは、必ずしもそれ自体で 1 文字を表すとは限りません。A single char instance by itself doesn't necessarily represent a character.

1 つの文字にマップされる char のペアは、"サロゲート ペア" と呼ばれます。The char pairs that map to a single character are called surrogate pairs. それらがどのように動作するかを理解するには、Unicode と UTF-16 のエンコードについて理解する必要があります。To understand how they work, you need to understand Unicode and UTF-16 encoding.

Unicode コード ポイントUnicode code points

Unicode は、さまざまなプラットフォーム上で、さまざまな言語とスクリプトで使用するための国際エンコード標準です。Unicode is an international encoding standard for use on various platforms and with various languages and scripts.

Unicode 標準では、110 万個を超えるコード ポイントが定義されています。The Unicode Standard defines over 1.1 million code points. コード ポイントは、0 から U+10FFFF (10 進数の 1,114,111) までの範囲の整数値です。A code point is an integer value that can range from 0 to U+10FFFF (decimal 1,114,111). 一部のコード ポイントは、文字、記号、または絵文字に割り当てられています。Some code points are assigned to letters, symbols, or emoji. その他は、新しい行に進むなど、テキストや文字の表示方法を制御するアクションに割り当てられています。Others are assigned to actions that control how text or characters are displayed, such as advance to a new line. 多くのコード ポイントはまだ割り当てられていません。Many code points are not yet assigned.

コード ポイントの割り当てのいくつかの例と、それらが記載されている Unicode 表へのリンクを次に示します。Here are some examples of code point assignments, with links to Unicode charts in which they appear:

Decimal (10 進数型)Decimal HexHex Example 説明Description
1010 U+000A N/AN/A 改行LINE FEED
6565 U+0061 aa ラテン小文字 ALATIN SMALL LETTER A
562562 U+0232 ȲȲ 長音符号付きラテン大文字 YLATIN CAPITAL LETTER Y WITH MACRON
68,67568,675 U+10C43 𐱃𐱃 古テュルク語文字オルホン ATOLD TURKIC LETTER ORKHON AT
127,801127,801 U+1F339 🌹🌹 バラの絵文字ROSE emoji

コード ポイントは、習慣的に、U+xxxx 構文を使用して参照されます。xxxx は 16 進エンコードの整数値です。Code points are customarily referred to by using the syntax U+xxxx, where xxxx is the hex-encoded integer value.

コード ポイントの全範囲内には、次の 2 つの部分範囲があります。Within the full range of code points there are two subranges:

  • U+0000..U+FFFF の範囲の基本多言語面 (BMP)The Basic Multilingual Plane (BMP) in the range U+0000..U+FFFF. この 16 ビットの範囲によって 65,536 個のコード ポイントが提供され、世界の書記体系の大部分を十分にカバーできます。This 16-bit range provides 65,536 code points, enough to cover the majority of the world's writing systems.
  • U+10000..U+10FFFF の範囲の補助コード ポイントSupplementary code points in the range U+10000..U+10FFFF. この 21 ビットの範囲によって、100 万個を超える追加のコード ポイントが提供されます。これは、あまり一般的ではない言語や、絵文字などのその他の目的のために使用できます。This 21-bit range provides more than a million additional code points that can be used for less well-known languages and other purposes such as emojis.

次の図は、BMP と補助コード ポイントの関係を示しています。The following diagram illustrates the relationship between the BMP and the supplementary code points.

BMP と補助コード ポイント

UTF-16 コード単位UTF-16 code units

16 ビット Unicode Transformation Format (UTF-16) は、16 ビットの "コード単位" を使用して Unicode コード ポイントを表す、文字エンコード システムです。16-bit Unicode Transformation Format (UTF-16) is a character encoding system that uses 16-bit code units to represent Unicode code points. .NET では、string 内のテキストをエンコードするために UTF-16 が使用されています。.NET uses UTF-16 to encode the text in a string. char インスタンスは、16 ビットのコード単位を表します。A char instance represents a 16-bit code unit.

1 つの 16 ビット コード単位は、基本多言語面の 16 ビット範囲に含まれる任意のコード ポイントを表すことができます。A single 16-bit code unit can represent any code point in the 16-bit range of the Basic Multilingual Plane. ただし、補助範囲内のコード ポイントについては、2 つの char インスタンスが必要です。But for a code point in the supplementary range, two char instances are needed.

サロゲート ペアSurrogate pairs

2 つの 16 ビット値から 1 つの 21 ビット値への変換は、U+D800 から U+DFFF (10 進数の 55,296 から 57,343) まで (境界も含める) の、"サロゲート コード ポイント" と呼ばれる特殊な範囲を使用して行われます。The translation of two 16-bit values to a single 21-bit value is facilitated by a special range called the surrogate code points, from U+D800 to U+DFFF (decimal 55,296 to 57,343), inclusive.

次の図は、BMP とサロゲート コード ポイントの関係を示しています。The following diagram illustrates the relationship between the BMP and the surrogate code points.

BMP とサロゲート コード ポイント

"上位サロゲート" コード ポイント (U+D800..U+DBFF) の直後に "下位サロゲート" コード ポイント (U+DC00..U+DFFF) が続く場合、そのペアは、次の数式を使用して補助コード ポイントとして解釈されます。When a high surrogate code point (U+D800..U+DBFF) is immediately followed by a low surrogate code point (U+DC00..U+DFFF), the pair is interpreted as a supplementary code point by using the following formula:

code point = 0x10000 +
  ((high surrogate code point - 0xD800) * 0x0400) +
  (low surrogate code point - 0xDC00)

10 進表記を使用した同じ数式を次に示します。Here's the same formula using decimal notation:

code point = 65,536 +
  ((high surrogate code point - 55,296) * 1,024) +
  (low surrogate code point - 56,320)

"上位" サロゲート コード ポイントには、"下位" サロゲート コード ポイントよりも大きい数値は含まれません。A high surrogate code point doesn't have a higher number value than a low surrogate code point. 上位サロゲート コード ポイントが "上位" と呼ばれる理由は、完全な 21 ビットのコード ポイント範囲の上位 11 ビットを計算するために使用されるからです。The high surrogate code point is called "high" because it's used to calculate the higher-order 11 bits of the full 21-bit code point range. 下位サロゲート コード ポイントは、下位 10 ビットを計算するために使用されます。The low surrogate code point is used to calculate the lower-order 10 bits.

たとえば、サロゲート ペア 0xD83C0xDF39 に対応する実際のコード ポイントは、次のように計算されます。For example, the actual code point that corresponds to the surrogate pair 0xD83C and 0xDF39 is computed as follows:

actual = 0x10000 + ((0xD83C - 0xD800) * 0x0400) + (0xDF39 - 0xDC00)
       = 0x10000 + (          0x003C  * 0x0400) +           0x0339
       = 0x10000 +                      0xF000  +           0x0339
       = 0x1F339

10 進表記を使用した同じ計算を次に示します。Here's the same calculation using decimal notation:

actual =  65,536 + ((55,356 - 55,296) * 1,024) + (57,145 - 56320)
       =  65,536 + (              60  * 1,024) +             825
       =  65,536 +                     61,440  +             825
       = 127,801

前の例では、"\ud83c\udf39" が、前述の U+1F339 ROSE ('🌹') コード ポイントの UTF-16 エンコードであることが示されています。The preceding example demonstrates that "\ud83c\udf39" is the UTF-16 encoding of the U+1F339 ROSE ('🌹') code point mentioned earlier.

Unicode スカラー値Unicode scalar values

Unicode スカラー値という用語は、サロゲート コード ポイント以外のすべてのコード ポイントを指します。The term Unicode scalar value refers to all code points other than the surrogate code points. つまり、スカラー値とは、文字が割り当てられているか、将来文字が割り当てられる可能性があるコード ポイントです。In other words, a scalar value is any code point that is assigned a character or can be assigned a character in the future. ここでの "文字" は、コード ポイントに割り当てることができるすべてのものを指します。これには、テキストや文字の表示方法を制御するアクションなどが含まれます。"Character" here refers to anything that can be assigned to a code point, which includes such things as actions that control how text or characters are displayed.

次の図は、スカラー値のコード ポイントを示しています。The following diagram illustrates the scalar value code points.

スカラー値

スカラー値としての Rune 型The Rune type as a scalar value

.NET Core 3.0 以降では、System.Text.Rune 型によって Unicode スカラー値が表されます。Beginning with .NET Core 3.0, the System.Text.Rune type represents a Unicode scalar value. Rune は、.NET Core 2.x または .NET Framework 4.x では使用できません。Rune is not available in .NET Core 2.x or .NET Framework 4.x.

Rune コンストラクターでは、生成されるインスタンスが有効な Unicode スカラー値であることが検証されます。そうでない場合は、例外がスローされます。The Rune constructors validate that the resulting instance is a valid Unicode scalar value, otherwise they throw an exception. 次の例は、Rune インスタンスのインスタンス化に成功するコードを示しています。入力が有効なスカラー値を表しているためです。The following example shows code that successfully instantiates Rune instances because the input represents valid scalar values:

Rune a = new Rune('a');
Rune b = new Rune(0x0061);
Rune c = new Rune('\u0061');
Rune d = new Rune(0x10421);
Rune e = new Rune('\ud801', '\udc21');

次の例では、例外がスローされます。コード ポイントがサロゲートの範囲内にあり、サロゲート ペアの一部ではないためです。The following example throws an exception because the code point is in the surrogate range and isn't part of a surrogate pair:

Rune f = new Rune('\ud801');

次の例では、コード ポイントが補助範囲を超えているため、例外がスローされます。The following example throws an exception because the code point is beyond the supplementary range:

Rune g = new Rune(0x12345678);

Rune の使用例: 大文字と小文字の変更 usage example: changing letter case

char を受け取り、スカラー値であるコード ポイントを操作していると仮定する API は、その char がサロゲート ペアのものであった場合、正しく動作しません。An API that takes a char and assumes it is working with a code point that is a scalar value doesn't work correctly if the char is from a surrogate pair. たとえば、string に含まれる各 char に対して Char.ToUpperInvariant を呼び出す、次のようなメソッドを考えてみます。For example, consider the following method that calls Char.ToUpperInvariant on each char in a string:

// THE FOLLOWING METHOD SHOWS INCORRECT CODE.
// DO NOT DO THIS IN A PRODUCTION APPLICATION.
static string ConvertToUpperBadExample(string input)
{
    StringBuilder builder = new StringBuilder(input.Length);
    for (int i = 0; i < input.Length; i++) /* or 'foreach' */
    {
        builder.Append(char.ToUpperInvariant(input[i]));
    }
    return builder.ToString();
}

input string に小文字のデザレット文字 er (𐑉) が含まれていた場合、このコードでは大文字 (𐐡) に変換することができません。If the input string contains the lowercase Deseret letter er (𐑉), this code won't convert it to uppercase (𐐡). このコードでは、U+D801U+DC49 の各サロゲート コード ポイントに対して、個別に char.ToUpperInvariant が呼び出されます。The code calls char.ToUpperInvariant separately on each surrogate code point, U+D801 and U+DC49. しかし、U+D801 自体には、それを小文字として識別するのに十分な情報が含まれていないため、char.ToUpperInvariant による変更は行われません。But U+D801 doesn't have enough information by itself to identify it as a lowercase letter, so char.ToUpperInvariant leaves it alone. また、U+DC49 も同じ方法で処理されます。And it handles U+DC49 the same way. その結果、input string に含まれる小文字の '𐑉' は、大文字の '𐐡' に変換されません。The result is that lowercase '𐑉' in the input string doesn't get converted to uppercase '𐐡'.

string を適切に大文字に変換するための 2 つのオプションを次に示します。Here are two options for correctly converting a string to uppercase:

  • char から char へと反復処理するのではなく、入力 string に対して String.ToUpperInvariant を呼び出します。Call String.ToUpperInvariant on the input string rather than iterating char-by-char. string.ToUpperInvariant メソッドは各サロゲート ペアの両方の部分にアクセスできるため、すべての Unicode コード ポイントを正しく処理できます。The string.ToUpperInvariant method has access to both parts of each surrogate pair, so it can handle all Unicode code points correctly.

  • 次の例に示すように、char インスタンスではなく Rune インスタンスとして Unicode スカラー値を反復処理します。Iterate through the Unicode scalar values as Rune instances instead of char instances, as shown in the following example. Rune インスタンスは有効な Unicode スカラー値であるため、スカラー値に対して動作することが想定されている API に渡すことができます。Since a Rune instance is a valid Unicode scalar value, it can be passed to APIs that expect to operate on a scalar value. たとえば、次の例に示すように Rune.ToUpperInvariant を呼び出すと、正しい結果が得られます。For example, calling Rune.ToUpperInvariant as shown in the following example gives correct results:

    static string ConvertToUpper(string input)
    {
        StringBuilder builder = new StringBuilder(input.Length);
        foreach (Rune rune in input.EnumerateRunes())
        {
            builder.Append(Rune.ToUpperInvariant(rune));
        }
        return builder.ToString();
    }
    

その他の Rune APIOther Rune APIs

Rune 型では、多くの char API と類似した機能が公開されています。The Rune type exposes analogs of many of the char APIs. たとえば、以下のメソッドは、char 型の静的 API に対応しています。For example, the following methods mirror static APIs on the char type:

Rune インスタンスから生のスカラー値を取得するには、Rune.Value プロパティを使用します。To get the raw scalar value from a Rune instance, use the Rune.Value property.

Rune インスタンスを再び char のシーケンスに変換するには、Rune.ToString または Rune.EncodeToUtf16 メソッドを使用します。To convert a Rune instance back to a sequence of chars, use Rune.ToString or the Rune.EncodeToUtf16 method.

すべての Unicode スカラー値は 1 つの char またはサロゲート ペアによって表すことができるため、あらゆる Rune インスタンスは多くても 2 つの char インスタンスによって表すことができます。Since any Unicode scalar value is representable by a single char or by a surrogate pair, any Rune instance can be represented by at most 2 char instances. Rune インスタンスを表すために必要な char インスタンスの数を確認するには、Rune.Utf16SequenceLength を使用します。Use Rune.Utf16SequenceLength to see how many char instances are required to represent a Rune instance.

.NET の Rune 型の詳細については、Rune API リファレンスを参照してください。For more information about the .NET Rune type, see the Rune API reference.

書記素クラスターGrapheme clusters

1 つの文字に見えるものが、複数のコード ポイントの組み合わせから生成されている場合があります。このため、"文字" の代わりに使用されることが多い、書記素クラスターというよりわかりやすい用語があります。What looks like one character might result from a combination of multiple code points, so a more descriptive term that is often used in place of "character" is grapheme cluster. .NET における同等の用語は、テキスト要素です。The equivalent term in .NET is text element.

次の string インスタンスについて考えてみましょう: "a"、"á"。Consider the string instances "a", "á". "á"、"👩🏽‍🚒"。"á", and "👩🏽‍🚒". お使いのオペレーティング システムによってこれらが Unicode 標準で指定されているとおりに処理される場合、これらの各 string インスタンスは、1 つのテキスト要素、または書記素クラスターとして表示されます。If your operating system handles them as specified by the Unicode standard, each of these string instances appears as a single text element or grapheme cluster. ただし、最後の 2 つは、複数のスカラー値コード ポイントによって表されます。But the last two are represented by more than one scalar value code point.

  • string "a" は 1 つのスカラー値で表され、1 つの char インスタンスが含まれます。The string "a" is represented by one scalar value and contains one char instance.

    • U+0061 LATIN SMALL LETTER A
  • string "á" は 1 つのスカラー値で表され、1 つの char インスタンスが含まれます。The string "á" is represented by one scalar value and contains one char instance.

    • U+00E1 LATIN SMALL LETTER A WITH ACUTE
  • string "á" は、"á" と同じように見えますが、2 つのスカラー値で表され、2 つの char インスタンスが含まれます。The string "á" looks the same as "á" but is represented by two scalar values and contains two char instances.

    • U+0061 LATIN SMALL LETTER A
    • U+0301 COMBINING ACUTE ACCENT
  • 最後に、string "👩🏽‍🚒" は 4 つのスカラー値で表され、7 つの char インスタンスが含まれます。Finally, the string "👩🏽‍🚒" is represented by four scalar values and contains seven char instances.

    • U+1F469 WOMAN (補助範囲、サロゲート ペアが必要)U+1F469 WOMAN (supplementary range, requires a surrogate pair)
    • U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4 (補助範囲、サロゲート ペアが必要)U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4 (supplementary range, requires a surrogate pair)
    • U+200D ZERO WIDTH JOINER
    • U+1F692 FIRE ENGINE (補助範囲、サロゲート ペアが必要)U+1F692 FIRE ENGINE (supplementary range, requires a surrogate pair)

前の例の一部 (結合アクセントの修飾子や肌の色の修飾子など) では、コード ポイントが画面にスタンドアロン要素として表示されません。In some of the preceding examples - such as the combining accent modifier or the skin tone modifier - the code point does not display as a standalone element on the screen. 代わりに、それは、その前に現れるテキスト要素の外観を変更する役割を果たしています。Rather, it serves to modify the appearance of a text element that came before it. これらの例は、1 つの "文字" または "書記素クラスター" と見なされるものを構成するために、複数のスカラー値が必要になる場合があることを示しています。These examples show that it might take multiple scalar values to make up what we think of as a single "character," or "grapheme cluster."

string の書記素クラスターを列挙するには、次の例に示すように StringInfo クラスを使用します。To enumerate the grapheme clusters of a string, use the StringInfo class as shown in the following example. Swift に慣れている場合、.NET の StringInfo 型は、概念的に Swift の characterと似ています。If you're familiar with Swift, the .NET StringInfo type is conceptually similar to Swift's character type.

例: char、Rune、テキスト要素のインスタンス数を数えるExample: count char, Rune, and text element instances

.NET API では、書記素クラスターは "テキスト要素" と呼ばれます。In .NET APIs, a grapheme cluster is called a text element. 次のメソッドは、string に含まれる charRune、およびテキスト要素のインスタンスの違いを示しています。The following method demonstrates the differences between char, Rune, and text element instances in a string:

static void PrintTextElementCount(string s)
{
    Console.WriteLine(s);
    Console.WriteLine($"Number of chars: {s.Length}");
    Console.WriteLine($"Number of runes: {s.EnumerateRunes().Count()}");

    TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(s);

    int textElementCount = 0;
    while (enumerator.MoveNext())
    {
        textElementCount++;
    }

    Console.WriteLine($"Number of text elements: {textElementCount}");
PrintTextElementCount("á");
// Number of chars: 1
// Number of runes: 1
// Number of text elements: 1

PrintTextElementCount("á");
// Number of chars: 2
// Number of runes: 2
// Number of text elements: 1

PrintTextElementCount("👩🏽‍🚒");
// Number of chars: 7
// Number of runes: 4
// Number of text elements: 1

.NET Framework または .NET Core 3.1 以前でこのコードを実行すると、絵文字のテキスト要素の数として 4 が表示されます。If you run this code in .NET Framework or .NET Core 3.1 or earlier, the text element count for the emoji shows 4. これは、.NET 5 で修正されている StringInfo クラスのバグが原因です。That is due to a bug in the StringInfo class that is fixed in .NET 5.

例: string インスタンスの分割Example: splitting string instances

string インスタンスを分割する場合は、サロゲート ペアと書記素クラスターを分割しないようにします。When splitting string instances, avoid splitting surrogate pairs and grapheme clusters. 不適切なコードを示す次の例について考えてみましょう。ここでは、string 内の 10 文字ごとに改行を挿入しようとしています。Consider the following example of incorrect code, which intends to insert line breaks every 10 characters in a string:

// THE FOLLOWING METHOD SHOWS INCORRECT CODE.
// DO NOT DO THIS IN A PRODUCTION APPLICATION.
static string InsertNewlinesEveryTencharsBadExample(string input)
{
    StringBuilder builder = new StringBuilder();

    // First, append chunks in multiples of 10 chars
    // followed by a newline.
    int i = 0;
    for (; i < input.Length - 10; i += 10)
    {
        builder.Append(input, i, 10);
        builder.AppendLine(); // newline
    }

    // Then append any leftover data followed by
    // a final newline.
    builder.Append(input, i, input.Length - i);
    builder.AppendLine(); // newline

    return builder.ToString();
}

このコードでは char インスタンスが列挙されているため、10-char の境界にまたがるサロゲート ペアは分割され、それらの間に改行が挿入されます。Because this code enumerates char instances, a surrogate pair that happens to straddle a 10-char boundary will be split and a newline injected between them. サロゲート コード ポイントはペアとしてのみ意味があるので、この挿入によってデータの破損が発生します。This insertion introduces data corruption, because surrogate code points are meaningful only as pairs.

データの破損の可能性は、char インスタンスではなく Rune インスタンス (スカラー値) を列挙してもなくなりません。The potential for data corruption isn't eliminated if you enumerate Rune instances (scalar values) instead of char instances. Rune インスタンスのセットによって、10-char の境界にまたがる書記素クラスターが構成される可能性があります。A set of Rune instances might make up a grapheme cluster that straddles a 10-char boundary. 書記素クラスターのセットが分割された場合は、正しく解釈できません。If the grapheme cluster set is split up, it can't be interpreted correctly.

より適切なアプローチは、次の例のように、書記素クラスター (またはテキスト要素) をカウントすることによって string を分割することです。A better approach is to break the string by counting grapheme clusters, or text elements, as in the following example:

static string InsertNewlinesEveryTenTextElements(string input)
{
    StringBuilder builder = new StringBuilder();

    // Append chunks in multiples of 10 chars

    TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(input);

    int textElementCount = 0;
    while (enumerator.MoveNext())
    {
        builder.Append(enumerator.Current);
        if (textElementCount % 10 == 0 && textElementCount > 0)
        {
            builder.AppendLine(); // newline
        }
        textElementCount++;
    }

    // Add a final newline.
    builder.AppendLine(); // newline
    return builder.ToString();

}

ただし、前述のように、.NET 5 以外の .NET の実装では、StringInfo クラスによって一部の書記素クラスターが不適切に処理される可能性があります。As noted earlier, however, in implementations of .NET other than .NET 5, the StringInfo class might handle some grapheme clusters incorrectly.

UTF-8 と UTF-32UTF-8 and UTF-32

前のセクションでは、UTF-16 に焦点を当てました。 .NET では string インスタンスをエンコードするために UTF-16 が使用されるためです。The preceding sections focused on UTF-16 because that's what .NET uses to encode string instances. Unicode には、他のエンコード システムもあります: UTF-8UTF-32 です。There are other encoding systems for Unicode - UTF-8 and UTF-32. これらのエンコードでは、8 ビットのコード単位と 32 ビットのコード単位がそれぞれ使用されます。These encodings use 8-bit code units and 32-bit code units, respectively.

UTF-16 と同様に、UTF-8 では、一部の Unicode スカラー値を表すために複数のコード単位が必要になります。Like UTF-16, UTF-8 requires multiple code units to represent some Unicode scalar values. UTF-32 では、1 つの 32 ビット コード単位で任意のスカラー値を表すことができます。UTF-32 can represent any scalar value in a single 32-bit code unit.

これら 3 つの各 Unicode エンコード システムで、同じ Unicode コード ポイントがどのように表されるかを示すいくつかの例を次に示します。Here are some examples showing how the same Unicode code point is represented in each of these three Unicode encoding systems:

Scalar: U+0061 LATIN SMALL LETTER A ('a')
UTF-8 : [ 61 ]           (1x  8-bit code unit  = 8 bits total)
UTF-16: [ 0061 ]         (1x 16-bit code unit  = 16 bits total)
UTF-32: [ 00000061 ]     (1x 32-bit code unit  = 32 bits total)

Scalar: U+0429 CYRILLIC CAPITAL LETTER SHCHA ('Щ')
UTF-8 : [ D0 A9 ]        (2x  8-bit code units = 16 bits total)
UTF-16: [ 0429 ]         (1x 16-bit code unit  = 16 bits total)
UTF-32: [ 00000429 ]     (1x 32-bit code unit  = 32 bits total)

Scalar: U+A992 JAVANESE LETTER GA ('ꦒ')
UTF-8 : [ EA A6 92 ]     (3x  8-bit code units = 24 bits total)
UTF-16: [ A992 ]         (1x 16-bit code unit  = 16 bits total)
UTF-32: [ 0000A992 ]     (1x 32-bit code unit  = 32 bits total)

Scalar: U+104CC OSAGE CAPITAL LETTER TSHA ('𐓌')
UTF-8 : [ F0 90 93 8C ]  (4x  8-bit code units = 32 bits total)
UTF-16: [ D801 DCCC ]    (2x 16-bit code units = 32 bits total)
UTF-32: [ 000104CC ]     (1x 32-bit code unit  = 32 bits total)

前述のように、サロゲート ペアに由来する 1 つの UTF-16 コード単位は、単独では意味がありません。As noted earlier, a single UTF-16 code unit from a surrogate pair is meaningless by itself. 同様に、1 つの UTF-8 コード単位が、スカラー値の計算に使用される 2 つ、3 つ、または 4 つのシーケンスに含まれている場合、それは単独では意味がありません。In the same way, a single UTF-8 code unit is meaningless by itself if it's in a sequence of two, three, or four used to calculate a scalar value.

エンディアンEndianness

.NET では、string の UTF-16 コード単位は、16 ビット整数 (char インスタンス) のシーケンスとして連続したメモリに格納されます。In .NET, the UTF-16 code units of a string are stored in contiguous memory as a sequence of 16-bit integers (char instances). 個々のコード単位のビットは、現在のアーキテクチャのエンディアンに従って並べられます。The bits of individual code units are laid out according to the endianness of the current architecture.

リトル エンディアンのアーキテクチャでは、UTF-16 コード ポイント [ D801 DCCC ] で構成される string は、バイト [ 0x01, 0xD8, 0xCC, 0xDC ] としてメモリ内に並べられます。On a little-endian architecture, the string consisting of the UTF-16 code points [ D801 DCCC ] would be laid out in memory as the bytes [ 0x01, 0xD8, 0xCC, 0xDC ]. ビッグ エンディアンのアーキテクチャでは、同じ string がバイト [ 0xD8, 0x01, 0xDC, 0xCC ] としてメモリ内に並べられます。On a big-endian architecture that same string would be laid out in memory as the bytes [ 0xD8, 0x01, 0xDC, 0xCC ].

相互に通信するコンピューター システムは、ネットワークを通過するデータの表現方法を一致させる必要があります。Computer systems that communicate with each other must agree on the representation of data crossing the wire. ほとんどのネットワーク プロトコルでは、テキストの送信時に UTF-8 が標準として使用されます。その理由の一部は、ビッグ エンディアンのコンピューターがリトル エンディアンのコンピューターと通信する際に発生する可能性のある問題を回避するためです。Most network protocols use UTF-8 as a standard when transmitting text, partly to avoid issues that might result from a big-endian machine communicating with a little-endian machine. UTF-8 コード ポイント [ F0 90 93 8C ] で構成される string は、エンディアンに関係なく常にバイト [ 0xF0, 0x90, 0x93, 0x8C ] として表されます。The string consisting of the UTF-8 code points [ F0 90 93 8C ] will always be represented as the bytes [ 0xF0, 0x90, 0x93, 0x8C ] regardless of endianness.

UTF-8 を使用してテキストを送信する場合、.NET アプリケーションでは、次の例のようなコードが使用されることがよくあります。To use UTF-8 for transmitting text, .NET applications often use code like the following example:

string stringToWrite = GetString();
byte[] stringAsUtf8Bytes = Encoding.UTF8.GetBytes(stringToWrite);
await outputStream.WriteAsync(stringAsUtf8Bytes, 0, stringAsUtf8Bytes.Length);

前の例では、メソッド Encoding.UTF8.GetBytes によって UTF-16 の string が一連の Unicode スカラー値にデコードされ、そのスカラー値が UTF-8 に再エンコードされて、生成されたシーケンスが byte の配列に配置されます。In the preceding example, the method Encoding.UTF8.GetBytes decodes the UTF-16 string back into a series of Unicode scalar values, then it re-encodes those scalar values into UTF-8 and places the resulting sequence into a byte array. メソッド Encoding.UTF8.GetString では反対の変換が実行されます。つまり、UTF-8 の byte 配列が UTF-16 の string に変換されます。The method Encoding.UTF8.GetString performs the opposite transformation, converting a UTF-8 byte array to a UTF-16 string.

警告

インターネット上では UTF-8 が一般的であるため、ネットワークから生バイトを読み取り、そのデータを UTF-8 であるかのように扱いたくなる場合があります。Since UTF-8 is commonplace on the internet, it may be tempting to read raw bytes from the wire and to treat the data as if it were UTF-8. しかし、それが本当に適切な形式であることを検証する必要があります。However, you should validate that it is indeed well-formed. 悪意のあるクライアントが、あなたのサービスに不適切な形式の UTF-8 を送信する可能性があります。A malicious client might submit ill-formed UTF-8 to your service. そのデータが適切な形式であるかのように操作された場合、アプリケーションにエラーやセキュリティ ホールが発生する可能性があります。If you operate on that data as if it were well-formed, it could cause errors or security holes in your application. UTF-8 のデータを検証するには、Encoding.UTF8.GetString のようなメソッドを使用できます。これは、受信データを string に変換するときに検証を実行します。To validate UTF-8 data, you can use a method like Encoding.UTF8.GetString, which will perform validation while converting the incoming data to a string.

適切な形式のエンコードWell-formed encoding

適切な形式の Unicode エンコードとは、明確に、かつエラーなしで Unicode スカラー値のシーケンスにデコードできる、コード単位の string のことです。A well-formed Unicode encoding is a string of code units that can be decoded unambiguously and without error into a sequence of Unicode scalar values. 適切な形式のデータは、UTF-8、UTF-16、および UTF-32 間で自由にトランスコードできます。Well-formed data can be transcoded freely back and forth between UTF-8, UTF-16, and UTF-32.

あるエンコード シーケンスが適切な形式であるかどうかは、コンピューターのアーキテクチャのエンディアンには関係ありません。The question of whether an encoding sequence is well-formed or not is unrelated to the endianness of a machine's architecture. 不適切な形式の UTF-8 シーケンスは、ビッグ エンディアンのコンピューターでもリトル エンディアンのコンピューターでも、同じように不適切な形式になります。An ill-formed UTF-8 sequence is ill-formed in the same way on both big-endian and little-endian machines.

不適切な形式のエンコードの例を、次にいくつか示します。Here are some examples of ill-formed encodings:

  • UTF-8 では、シーケンス [ 6C C2 61 ] は不適切な形式です。C2 の後に 61 を配置することはできないためです。In UTF-8, the sequence [ 6C C2 61 ] is ill-formed because C2 cannot be followed by 61.

  • UTF-16 では、シーケンス [ DC00 DD00 ] (または、C# では、string "\udc00\udd00") は不適切な形式です。下位サロゲート DC00 の後に別の下位サロゲート DD00 を配置することはできないためです。In UTF-16, the sequence [ DC00 DD00 ] (or, in C#, the string "\udc00\udd00") is ill-formed because the low surrogate DC00 cannot be followed by another low surrogate DD00.

  • UTF-32 では、シーケンス [ 0011ABCD ] は不適切な形式です。0011ABCD が Unicode スカラー値の範囲外であるためです。In UTF-32, the sequence [ 0011ABCD ] is ill-formed because 0011ABCD is outside the range of Unicode scalar values.

.NET では、string インスタンスにはほとんど常に適切な形式の UTF-16 データが含まれますが、保証されているわけではありません。In .NET, string instances almost always contain well-formed UTF-16 data, but that isn't guaranteed. string インスタンス内に不適切な形式の UTF-16 データを作成する有効な C# コードを、次の例に示します。The following examples show valid C# code that creates ill-formed UTF-16 data in string instances.

  • 不適切な形式のリテラル:An ill-formed literal:

    const string s = "\ud800";
    
  • サロゲート ペアを分割する部分文字列:A substring that splits up a surrogate pair:

    string x = "\ud83e\udd70"; // "🥰"
    string y = x.Substring(1, 1); // "\udd70" standalone low surrogate
    

Encoding.UTF8.GetString のような API を使用する場合、不適切な形式の string インスタンスが返されることはありません。APIs like Encoding.UTF8.GetString never return ill-formed string instances. Encoding.GetString および Encoding.GetBytes メソッドでは、入力に含まれる不適切な形式のシーケンスが検出され、出力を生成する際に文字の置換が実行されます。Encoding.GetString and Encoding.GetBytes methods detect ill-formed sequences in the input and perform character substitution when generating the output. たとえば、Encoding.ASCII.GetString(byte[]) への入力に非 ASCII バイト (U+0000..U+007F の範囲外) が含まれていた場合、返される string インスタンスには '?' が挿入されます。For example, if Encoding.ASCII.GetString(byte[]) sees a non-ASCII byte in the input (outside the range U+0000..U+007F), it inserts a '?' into the returned string instance. Encoding.UTF8.GetString(byte[]) によって返される string インスタンスでは、不適切な形式の UTF-8 シーケンスが U+FFFD REPLACEMENT CHARACTER ('�') に置き換えられます。Encoding.UTF8.GetString(byte[]) replaces ill-formed UTF-8 sequences with U+FFFD REPLACEMENT CHARACTER ('�') in the returned string instance. 詳細については、Unicode 標準の、セクション 5.22 および 3.9 を参照してください。For more information, see the Unicode Standard, Sections 5.22 and 3.9.

組み込みの Encoding クラスは、不適切な形式のシーケンスが検出されたときに、文字の置換を実行するのではなく、例外をスローするように構成することもできます。The built-in Encoding classes can also be configured to throw an exception rather than perform character substitution when ill-formed sequences are seen. この方法は、文字の置換が受け入れられない可能性がある、セキュリティに影響するアプリケーションでよく使用されます。This approach is often used in security-sensitive applications where character substitution might not be acceptable.

byte[] utf8Bytes = ReadFromNetwork();
UTF8Encoding encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
string asString = encoding.GetString(utf8Bytes); // will throw if 'utf8Bytes' is ill-formed

組み込みの Encoding クラスの使用方法について詳しくは、「.NET で文字エンコーディング クラスを使用する方法」をご覧ください。For information about how to use the built-in Encoding classes, see How to use character encoding classes in .NET.

関連項目See also