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:
- Char.GetNumericValue
- Char.GetUnicodeCategory
- Char.IsDigit
- Char.IsLetter
- Char.IsLetterOrDigit
- Char.IsLower
- Char.IsNumber
- Char.IsPunctuation
- Char.IsSymbol
- Char.IsUpper
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 char
kullanacak 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:
- Char.IsSurrogate
- Char.IsSurrogatePair
- Char.IsHighSurrogate
- Char.IsLowSurrogate
- Char.ConvertFromUtf32
- Char.ConvertToUtf32
Ö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 Rune
daha 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 char
bir 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 char
bir 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 Rune
arasında char
bazı API farklılıkları vardır. Örneğin:
- tanımına göre örnekler hiçbir zaman vekil kod noktaları olabileceğinden Char.IsSurrogate(Char)ile
Rune
eşdeğeri yokturRune
. - Rune.GetUnicodeCategory her zaman ile aynı sonucu Char.GetUnicodeCategorydöndürmez. ile aynı değeri CharUnicodeInfo.GetUnicodeCategorydöndürür. Daha fazla bilgi için, üzerinde açıklamalar bölümüne Char.GetUnicodeCategorybakın.
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 int32
bir 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.