System.Text.Rune yapısı

Bu makale, bu API'nin başvuru belgelerine ek açıklamalar sağlar.

Örnek Rune , Bir Unicode skaler değerini temsil eder; yani vekil aralığı (U+D800.. U+DFFF). Türün oluşturucuları ve dönüştürme işleçleri girişi doğrular, böylece tüketiciler temel alınan Rune örneğin iyi biçimlendirildiğini varsayarak API'leri çağırabilir.

Unicode skaler değeri, kod noktası, vekil aralık ve iyi biçimlendirilmiş terimleri bilmiyorsanız bkz . .NET'te karakter kodlamaya giriş.

Rune türü ne zaman kullanılır?

Kodunuz şu şekildeyse türünü kullanmayı Rune göz önünde bulundurun:

  • Unicode skaler değerleri gerektiren API'leri çağırır
  • Vekil çiftleri açıkça işler

Unicode skaler değerleri gerektiren API'ler

Kodunuz veya ReadOnlySpan<char>string içindeki örnekler arasında char yineleniyorsa, bazı char yöntemler vekil aralıktaki örneklerde char düzgün çalışmaz. Örneğin, aşağıdaki API'lerin düzgün çalışması için skaler bir değer char gerekir:

Aşağıdaki örnekte, örneklerden herhangi biri vekil kod noktalarıysa düzgün çalışmayan char kod gösterilmektedir:

// 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

Aşağıda, ile çalışan eşdeğer bir ReadOnlySpan<char>kod vardır:

// 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;
}

Yukarıdaki kod, İngilizce gibi bazı dillerde düzgün çalışır:

CountLettersInString("Hello")
// Returns 5

Ancak, Osage gibi Temel Çok Dilli Düzlem dışındaki diller için düzgün çalışmaz:

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

Bu yöntemin Osage metni için yanlış sonuçlar döndürmesinin nedeni, Osage harfleri örneklerinin char vekil kod noktaları olmasıdır. Hiçbir vekil kod noktası, bunun bir harf olup olmadığını belirlemek için yeterli bilgiye sahip değildir.

Bu kodu yerine charkullanacak Rune şekilde değiştirirseniz, yöntemi Temel Çok Dilli Düzlem dışındaki kod noktalarıyla doğru şekilde çalışır:

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

Aşağıda, ile çalışan eşdeğer bir ReadOnlySpan<char>kod vardır:

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

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

    return letterCount;
}

Yukarıdaki kod, işletim sistemi harflerini doğru sayar:

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

Vekil çiftleri açıkça işleyen kod

Kodunuz aşağıdaki yöntemler gibi vekil kod noktaları üzerinde açıkça çalışan API'leri çağırırsa türünü kullanmayı Rune göz önünde bulundurun:

Örneğin, aşağıdaki yöntemin vekil char çiftleriyle ilgilenmek için özel mantığı vardır:

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.");
        }
    }
}

Bu tür kod, aşağıdaki örnekte olduğu gibi kullanırsa Runedaha basittir:

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 ne zaman kullanılmamalı?

Kodunuz aşağıdaki gibiyse türünü kullanmanız Rune gerekmez:

  • Tam char eşleşmeleri arar
  • Bilinen bir karakter değerindeki bir dizeyi böler

Kodunuzun kullanılması Rune yanlış sonuçlar döndürebilir:

  • Bir içindeki görüntü karakterlerinin sayısını sayar string

Tam char eşleşmeleri arayın

Aşağıdaki kod, ilk eşleşmenin dizinini döndüren belirli karakterleri ararken yinelenir string . Kod tek charbir ile temsil edilen karakterleri aradığından, bu kodu kullanacak Runeşekilde değiştirmenize gerek yoktur.

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
}

Bilinen bir dizeyi bölme char

Aşağıdaki örnekte olduğu gibi (boşluk) veya ',' (virgül) gibi ' ' sınırlayıcıları çağırmak string.Split ve kullanmak yaygın bir durumdur:

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

Kod tek charbir ile temsil edilen karakterleri aradığından burada kullanılması Rune gerekmez.

Bir içindeki görüntü karakterlerinin sayısını sayma string

Dizedeki örnek sayısı Rune , dize görüntülenirken gösterilen kullanıcı tarafından algılanabilir karakterlerin sayısıyla eşleşmeyebilir.

Rune Örnekler Unicode skaler değerlerini temsil ettiğinden, Unicode metin kesimleme yönergelerini izleyen bileşenler, görüntüleme karakterlerini saymak için yapı taşı olarak kullanabilirRune.

Tür StringInfo , görüntüleme karakterlerini saymak için kullanılabilir, ancak .NET 5+ dışındaki .NET uygulamaları için tüm senaryolarda doğru sayılmaz.

Daha fazla bilgi için bkz . Grapheme kümeleri.

Örneğini oluşturma Rune

Örnek almanın Rune birkaç yolu vardır. Bir oluşturucuyu kullanarak doğrudan şu kaynaktan oluşturabilirsiniz Rune :

  • Bir kod noktası.

    Rune a = new Rune(0x0061); // LATIN SMALL LETTER A
    Rune b = new Rune(0x10421); // DESERET CAPITAL LETTER ER
    
  • Tek bir char.

    Rune c = new Rune('a');
    
  • char Vekil çift.

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

Giriş geçerli bir Unicode skaler değerini temsil etmiyorsa, oluşturucuların tümü bir ArgumentException oluşturur.

Rune.TryCreate Hata durumunda özel durumların atılmasını istemeyen arayanlar için kullanılabilir yöntemler vardır.

Rune örnekler mevcut giriş dizilerinden de okunabilir. Örneğin, UTF-16 verilerini temsil eden bir ReadOnlySpan<char> ver alındığında Rune.DecodeFromUtf16 , yöntem giriş aralığının başındaki ilk Rune örneği döndürür. yöntemi, Rune.DecodeFromUtf8 UTF-8 verilerini temsil eden bir ReadOnlySpan<byte> parametre kabul ederek benzer şekilde çalışır. Yayılma alanının başlangıcı yerine, yayılma alanının sonundan okunacak eşdeğer yöntemler vardır.

Sorgu özellikleri Rune

Bir Rune örneğin tamsayı kod noktası değerini almak için özelliğini kullanın Rune.Value .

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

Tür üzerinde char kullanılabilen statik API'lerin çoğu türü üzerinde Rune de kullanılabilir. Örneğin, Rune.IsWhiteSpace ve Rune.GetUnicodeCategoryChar.GetUnicodeCategory yöntemleriyle Char.IsWhiteSpace eşdeğerdir. Rune Yöntemler vekil çiftleri doğru şekilde işler.

Aşağıdaki örnek kod giriş ReadOnlySpan<char> olarak alır ve hem başlangıç hem de yayma aralığının sonundan harf veya basamak olmayan her Rune birini kırpır.

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;
}

ile Runearasında char bazı API farklılıkları vardır. Örneğin:

bir'i Rune UTF-8 veya UTF-16'ya dönüştürme

bir Rune Unicode skaler değeri olduğundan UTF-8, UTF-16 veya UTF-32 kodlamasına dönüştürülebilir. Türü, Rune UTF-8 ve UTF-16'ya dönüştürme için yerleşik desteğe sahiptir.

örneği Rune.EncodeToUtf16 örneklere char dönüştürürRune. Örneğin Rune UTF-16'ya dönüştürülmesinin sonucunda ortaya çıkan örnek sayısını char sorgulamak için özelliğini kullanınRune.Utf16SequenceLength. UTF-8 dönüştürmesi için benzer yöntemler vardır.

Aşağıdaki örnek bir Rune örneği diziye char dönüştürür. Kod, değişkeninde rune bir Rune örneğinin olduğunu varsayar:

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

a string , UTF-16 karakterlerinden oluşan bir dizi olduğundan, aşağıdaki örnek de bir Rune örneği UTF-16'ya dönüştürür:

string theString = rune.ToString();

Aşağıdaki örnek bir Rune örneği bayt UTF-8 dizisine dönüştürür:

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

Rune.EncodeToUtf16 ve Rune.EncodeToUtf8 yöntemleri, yazılan öğelerin gerçek sayısını döndürür. Hedef arabellek sonucu içeremeyecek kadar kısaysa bir özel durum oluşturur. Özel durumlardan kaçınmak isteyen arayanlar için oluşturma TryEncodeToUtf8TryEncodeToUtf16 ve yöntemleri yoktur.

.NET ile diğer diller karşılaştırması

"rune" terimi Unicode Standard'da tanımlanmamıştır. Terim UTF-8'in oluşturulmasına kadar uzanacak. Rob Pike ve Ken Thompson, sonunda kod noktası olarak neyin bilineceğini açıklamak için bir terim arıyorlardı. "Rune" terimine yerleştiler ve Rob Pike'ın go programlama dili üzerindeki daha sonraki etkisi terimin popülerleştirilmesine yardımcı oldu.

Ancak, .NET Rune türü Go rune türünün eşdeğeri değildir. Go'darune, türü için int32bir diğer addır. Go çalıştırması bir Unicode kod noktasını temsil etmek için tasarlanmıştır, ancak vekil kod noktaları ve yasal Unicode kod noktaları olmayan değerler de dahil olmak üzere herhangi bir 32 bit değer olabilir.

Diğer programlama dillerindeki benzer türler için Rust'ın ilkel türüne veya Swift'in Unicode.Scalar türüne (her ikisi de Unicode skaler değerlerini temsil eder) bakın.char bunlara benzer işlevler sağlarlar. NET'in Rune türü ve yasal Unicode skaler değerleri olmayan değerlerin örneğini oluşturulmasına izin vermez.