System.Text.Rune 構造体

この記事では、この API のリファレンス ドキュメントへの補足的な解説を提供します。

インスタンスは Rune Unicode スカラー値を表します。これは、サロゲート範囲 (U+D800.) を除く任意のコード ポイントを意味します。U+DFFF)。 型のコンストラクターと変換演算子は入力を検証するため、コンシューマーは基になる Rune インスタンスが整形式であると仮定して API を呼び出すことができます。

Unicode スカラー値、コード ポイント、サロゲート範囲、整形式の用語に慣れていない場合は、「.NET での文字エンコードの概要」を参照してください

Rune 型を使用する場合

コードが次の場合は、型の Rune 使用を検討してください。

  • Unicode スカラー値を必要とする API を呼び出す
  • サロゲート ペアを明示的に処理する

Unicode スカラー値を必要とする API

コードが a または a stringReadOnlySpan<char>のインスタンスをchar反復処理する場合、サロゲート範囲内のインスタンスでchar一部のcharメソッドが正しく動作しません。 たとえば、次の API では、スカラー値 char が正しく機能する必要があります。

次の例は、いずれかのインスタンスがサロゲート コード ポイントの場合に char 正しく動作しないコードを示しています。

// THE FOLLOWING METHOD SHOWS INCORRECT CODE.
// DO NOT DO THIS IN A PRODUCTION APPLICATION.
int CountLettersBadExample(string s)
{
    int letterCount = 0;

    foreach (char ch in s)
    {
        if (char.IsLetter(ch))
        { letterCount++; }
    }

    return letterCount;
}
// THE FOLLOWING METHOD SHOWS INCORRECT CODE.
// DO NOT DO THIS IN A PRODUCTION APPLICATION.
let countLettersBadExample (s: string) =
    let mutable letterCount = 0

    for ch in s do
        if Char.IsLetter ch then
            letterCount <- letterCount + 1
    
    letterCount

以下に、以下で動作する同等のコードを ReadOnlySpan<char>示します。

// THE FOLLOWING METHOD SHOWS INCORRECT CODE.
// DO NOT DO THIS IN A PRODUCTION APPLICATION.
static int CountLettersBadExample(ReadOnlySpan<char> span)
{
    int letterCount = 0;

    foreach (char ch in span)
    {
        if (char.IsLetter(ch))
        { letterCount++; }
    }

    return letterCount;
}

上記のコードは、英語などの一部の言語で正しく動作します。

CountLettersInString("Hello")
// Returns 5

ただし、Osage などの基本多言語プレーン以外の言語では正しく機能しません。

CountLettersInString("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟")
// Returns 0

このメソッドが Osage テキストの正しくない結果を返す理由は、Osage 文字の char インスタンスがサロゲート コード ポイントであるためです。 1 つのサロゲート コード ポイントには、文字かどうかを判断するのに十分な情報がありません。

代わりにchar使用Runeするようにこのコードを変更した場合、メソッドは Basic 多言語プレーンの外部のコード ポイントで正しく動作します。

int CountLetters(string s)
{
    int letterCount = 0;

    foreach (Rune rune in s.EnumerateRunes())
    {
        if (Rune.IsLetter(rune))
        { letterCount++; }
    }

    return letterCount;
}
let countLetters (s: string) =
    let mutable letterCount = 0

    for rune in s.EnumerateRunes() do
        if Rune.IsLetter rune then
            letterCount <- letterCount + 1

    letterCount

以下に、以下で動作する同等のコードを ReadOnlySpan<char>示します。

static int CountLetters(ReadOnlySpan<char> span)
{
    int letterCount = 0;

    foreach (Rune rune in span.EnumerateRunes())
    {
        if (Rune.IsLetter(rune))
        { letterCount++; }
    }

    return letterCount;
}

上記のコードでは、Osage 文字が正しくカウントされます。

CountLettersInString("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟")
// Returns 8

サロゲート ペアを明示的に処理するコード

次のメソッドなど、サロゲート コード ポイントを明示的に操作する API をコードが呼び出す場合は、この型の使用 Rune を検討してください。

たとえば、次のメソッドには、サロゲート char ペアを処理するための特別なロジックがあります。

static void ProcessStringUseChar(string s)
{
    Console.WriteLine("Using char");

    for (int i = 0; i < s.Length; i++)
    {
        if (!char.IsSurrogate(s[i]))
        {
            Console.WriteLine($"Code point: {(int)(s[i])}");
        }
        else if (i + 1 < s.Length && char.IsSurrogatePair(s[i], s[i + 1]))
        {
            int codePoint = char.ConvertToUtf32(s[i], s[i + 1]);
            Console.WriteLine($"Code point: {codePoint}");
            i++; // so that when the loop iterates it's actually +2
        }
        else
        {
            throw new Exception("String was not well-formed UTF-16.");
        }
    }
}

次の例のように、このようなコードを使用 Runeする方が簡単です。

static void ProcessStringUseRune(string s)
{
    Console.WriteLine("Using Rune");

    for (int i = 0; i < s.Length;)
    {
        if (!Rune.TryGetRuneAt(s, i, out Rune rune))
        {
            throw new Exception("String was not well-formed UTF-16.");
        }

        Console.WriteLine($"Code point: {rune.Value}");
        i += rune.Utf16SequenceLength; // increment the iterator by the number of chars in this Rune
    }
}

どのようなときに Rune を使用しないか

コードが次の場合は、型を Rune 使用する必要はありません。

  • 完全 char 一致を検索します
  • 既知の文字値で文字列を分割します

コードが次の場合、型を Rune 使用すると正しくない結果が返される可能性があります。

  • 内の表示文字数をカウントします。 string

完全 char 一致を探す

次のコードは、特定の文字の検索を string 反復処理し、最初の一致のインデックスを返します。 コードが 1 つのchar文字で表される文字を検索するため、このコードを使用Runeするように変更する必要はありません。

int GetIndexOfFirstAToZ(string s)
{
    for (int i = 0; i < s.Length; i++)
    {
        char thisChar = s[i];
        if ('A' <= thisChar && thisChar <= 'Z')
        {
            return i; // found a match
        }
    }

    return -1; // didn't find 'A' - 'Z' in the input string
}

既知の文字列を分割する char

次の例のように、(スペース) や ',' (コンマ) などの' '区切り記号を呼び出string.Splitして使用するのが一般的です。

string inputString = "🐂, 🐄, 🐆";
string[] splitOnSpace = inputString.Split(' ');
string[] splitOnComma = inputString.Split(',');

コードは単一charの文字で表される文字を探しているので、ここで使用Runeする必要はありません。

内の表示文字数をカウントします。 string

文字列内のインスタンスの Rune 数が、文字列を表示するときに表示されるユーザーが認識できる文字の数と一致しない可能性があります。

インスタンスは Unicode スカラー値を表しているためRune、Unicode テキストのセグメント化ガイドライン従うコンポーネントは、表示文字をカウントするための構成要素として使用Runeできます。

この型を StringInfo 使用して表示文字をカウントできますが、.NET 5 以降以外の .NET 実装のすべてのシナリオでは正しくカウントされません。

詳細については、「Grapheme クラスター」を参照してください

をインスタンス化する方法 Rune

インスタンスを取得 Rune するには、いくつかの方法があります。 コンストラクターを使用して、次の場所から直接作成 Rune できます。

  • コード ポイント。

    Rune a = new Rune(0x0061); // LATIN SMALL LETTER A
    Rune b = new Rune(0x10421); // DESERET CAPITAL LETTER ER
    
  • 1つの char

    Rune c = new Rune('a');
    
  • サロゲート char ペア。

    Rune d = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL
    

すべてのコンストラクターは、入力が有効な Unicode スカラー値を表していない場合に an をスロー ArgumentException します。

失敗した場合に Rune.TryCreate 例外をスローしたくない呼び出し元に使用できるメソッドがあります。

Rune インスタンスは、既存の入力シーケンスから読み取ることもできます。 たとえば、UTF-16 データを表す a ReadOnlySpan<char> を指定すると、メソッドは Rune.DecodeFromUtf16 入力スパンの先頭にある最初 Rune のインスタンスを返します。 このメソッドも Rune.DecodeFromUtf8 同様に動作し ReadOnlySpan<byte> 、UTF-8 データを表すパラメーターを受け入れます。 スパンの先頭ではなく、スパンの末尾から読み取る同等のメソッドがあります。

のクエリ プロパティ Rune

インスタンスの整数コード ポイント値を Rune 取得するには、プロパティを Rune.Value 使用します。

Rune rune = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL
int codePoint = rune.Value; // = 128302 decimal (= 0x1F52E)

型で使用できる char 静的 API の多くは、型でも使用できます Rune 。 たとえば、Rune.IsWhiteSpaceメソッドとRune.GetUnicodeCategoryChar.GetUnicodeCategory同等Char.IsWhiteSpaceです。 メソッドは Rune サロゲート ペアを正しく処理します。

次のコード例では、入力として a を ReadOnlySpan<char> 受け取り、スパンの先頭と末尾の両方から文字や数字ではない部分 Rune をトリミングします。

static ReadOnlySpan<char> TrimNonLettersAndNonDigits(ReadOnlySpan<char> span)
{
    // First, trim from the front.
    // If any Rune can't be decoded
    // (return value is anything other than "Done"),
    // or if the Rune is a letter or digit,
    // stop trimming from the front and
    // instead work from the end.
    while (Rune.DecodeFromUtf16(span, out Rune rune, out int charsConsumed) == OperationStatus.Done)
    {
        if (Rune.IsLetterOrDigit(rune))
        { break; }
        span = span[charsConsumed..];
    }

    // Next, trim from the end.
    // If any Rune can't be decoded,
    // or if the Rune is a letter or digit,
    // break from the loop, and we're finished.
    while (Rune.DecodeLastFromUtf16(span, out Rune rune, out int charsConsumed) == OperationStatus.Done)
    {
        if (Rune.IsLetterOrDigit(rune))
        { break; }
        span = span[..^charsConsumed];
    }

    return span;
}

charRune. 次に例を示します。

a Rune から UTF-8 または UTF-16 への変換

a Rune は Unicode スカラー値であるため、UTF-8、UTF-16、または UTF-32 エンコードに変換できます。 この Rune 型には、UTF-8 および UTF-16 への変換が組み込まれています。

インスタンス Rune.EncodeToUtf16Rune インスタンスに char 変換します。 インスタンスを UTF-16 に変換した結果として発生するRuneインスタンスのchar数を照会するには、このプロパティをRune.Utf16SequenceLength使用します。 UTF-8 変換にも同様のメソッドが存在します。

次の例では、インスタンスを Rune 配列に char 変換します。 このコードでは、変数に Rune インスタンス rune があることを前提としています。

char[] chars = new char[rune.Utf16SequenceLength];
int numCharsWritten = rune.EncodeToUtf16(chars);

a string は UTF-16 文字のシーケンスであるため、次の例ではインスタンスも UTF-16 に変換 Rune します。

string theString = rune.ToString();

次の例では、インスタンスを Rune バイト配列に UTF-8 変換します。

byte[] bytes = new byte[rune.Utf8SequenceLength];
int numBytesWritten = rune.EncodeToUtf8(bytes);

and Rune.EncodeToUtf8 メソッドはRune.EncodeToUtf16、書き込まれた要素の実際の数を返します。 ターゲット バッファーが短すぎて結果を含められなかった場合は、例外がスローされます。 例外を TryEncodeToUtf8 回避したい呼び出し TryEncodeToUtf16 元には、スローやメソッドはありません。

.NET でのルーンと他の言語

"rune" という用語は、Unicode 標準では定義されていません。 この用語は UTF-8 の作成に遡ります。 Rob Pike と Ken Thompson は、最終的に何がコード ポイントとして知られるかを説明する用語を探していました。 彼らは「ルーン」という用語に落ち着き、後にGoプログラミング言語に対するロブ・パイクの影響が用語の普及に役立ちました。

ただし、.NET Rune 型は Go rune 型と同等ではありません。 Go では、この rune 型は 〗の エイリアス int32です。 Go ルーンは Unicode コード ポイントを表すことを目的としていますが、サロゲート コード ポイントや有効な Unicode コード ポイントではない値など、任意の 32 ビット値を指定できます。

他のプログラミング言語でも同様の型については、Rust のプリミティブ型または Swift のUnicode.Scalarを参照してください。どちらも Unicode スカラー値を表charします。 これらは、次のような機能を提供します。NET の Rune 型であり、有効な Unicode スカラー値ではない値のインスタンス化を禁止します。