System.Text.Rune, struktura
Ten artykuł zawiera dodatkowe uwagi dotyczące dokumentacji referencyjnej dla tego interfejsu API.
Rune Wystąpienie reprezentuje wartość skalarną Unicode, co oznacza, że dowolny punkt kodu z wyłączeniem zakresu zastępczego (U+D800.). U+DFFF). Konstruktory i operatory konwersji typu weryfikują dane wejściowe, aby użytkownicy mogli wywoływać interfejsy API przy założeniu, że wystąpienie bazowe Rune jest prawidłowo sformułowane.
Jeśli nie znasz terminów Wartość skalarna Unicode, punkt kodu, zakres zastępczy i dobrze sformułowany, zobacz Wprowadzenie do kodowania znaków na platformie .NET.
Kiedy należy użyć typu Rune
Rozważ użycie Rune
typu, jeśli kod:
- Wywołuje interfejsy API, które wymagają wartości skalarnych Unicode
- Jawnie obsługuje pary zastępcze
Interfejsy API wymagające wartości skalarnych Unicode
Jeśli kod wykonuje iterację po char
wystąpieniach w obiekcie string
lub ReadOnlySpan<char>
, niektóre char
metody nie będą działać poprawnie w char
wystąpieniach, które znajdują się w zakresie zastępczym. Na przykład następujące interfejsy API wymagają poprawnego działania wartości char
skalarnej:
- Char.GetNumericValue
- Char.GetUnicodeCategory
- Char.IsDigit
- Char.IsLetter
- Char.IsLetterOrDigit
- Char.IsLower
- Char.IsNumber
- Char.IsPunctuation
- Char.IsSymbol
- Char.IsUpper
W poniższym przykładzie pokazano kod, który nie będzie działał poprawnie, jeśli którykolwiek z char
wystąpień jest zastępczymi punktami kodu:
// 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
Oto równoważny kod, który działa z elementem 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;
}
Powyższy kod działa poprawnie w niektórych językach, takich jak angielski:
CountLettersInString("Hello")
// Returns 5
Jednak nie będzie działać poprawnie w przypadku języków spoza podstawowej płaszczyzny wielojęzycznej, takiej jak Osage:
CountLettersInString("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟")
// Returns 0
Powodem, dla którego ta metoda zwraca nieprawidłowe wyniki dla tekstu systemu operacyjnego, jest to, że char
wystąpienia liter systemu operacyjnego są punktami kodu zastępczymi. Żaden pojedynczy punkt kodu zastępczego nie ma wystarczającej ilości informacji, aby ustalić, czy jest to litera.
Jeśli zmienisz ten kod tak, aby używał zamiast Rune
char
metody , metoda działa poprawnie z punktami kodu poza podstawową płaszczyzną wielojęzyczną:
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
Oto równoważny kod, który działa z elementem ReadOnlySpan<char>
:
static int CountLetters(ReadOnlySpan<char> span)
{
int letterCount = 0;
foreach (Rune rune in span.EnumerateRunes())
{
if (Rune.IsLetter(rune))
{ letterCount++; }
}
return letterCount;
}
Powyższy kod poprawnie zlicza litery Osage:
CountLettersInString("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟")
// Returns 8
Kod, który jawnie obsługuje pary zastępcze
Rozważ użycie Rune
typu, jeśli kod wywołuje interfejsy API, które jawnie działają w punktach kodu zastępczego, takich jak następujące metody:
- Char.IsSurrogate
- Char.IsSurrogatePair
- Char.IsHighSurrogate
- Char.IsLowSurrogate
- Char.ConvertFromUtf32
- Char.ConvertToUtf32
Na przykład następująca metoda ma specjalną logikę do obsługi par zastępczych 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.");
}
}
}
Taki kod jest prostszy, jeśli używa Rune
metody , jak w poniższym przykładzie:
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
}
}
Kiedy nie używać atrybutu Rune
Nie musisz używać Rune
typu, jeśli kod:
- Szuka dokładnych
char
dopasowań - Dzieli ciąg na znanej wartości char
Rune
Użycie typu może zwracać nieprawidłowe wyniki, jeśli kod:
- Zlicza liczbę znaków wyświetlanych w obiekcie
string
Wyszukiwanie dokładnych char
dopasowań
Poniższy kod iteruje wyszukiwanie string
określonych znaków, zwracając indeks pierwszego dopasowania. Nie trzeba zmieniać tego kodu tak, aby używał Rune
metody , ponieważ kod szuka znaków reprezentowanych przez pojedynczy char
element .
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
}
Dzielenie ciągu na znany char
Często należy wywoływać string.Split
ograniczniki, takie jak ' '
(spacja) lub ','
(przecinek), jak w poniższym przykładzie:
string inputString = "🐂, 🐄, 🐆";
string[] splitOnSpace = inputString.Split(' ');
string[] splitOnComma = inputString.Split(',');
Nie ma tutaj potrzeby użycia Rune
, ponieważ kod szuka znaków reprezentowanych przez pojedynczy char
element .
Zlicz liczbę znaków wyświetlanych w obiekcie string
Liczba Rune
wystąpień w ciągu może być niezgodna z liczbą znaków, które można zobaczyć podczas wyświetlania ciągu.
Ponieważ Rune
wystąpienia reprezentują wartości skalarne Unicode, składniki zgodne z wytycznymi dotyczącymi segmentacji tekstu Unicode mogą być używane Rune
jako blok konstrukcyjny do zliczania znaków wyświetlanych.
Typ StringInfo może służyć do zliczania znaków wyświetlania, ale nie jest on poprawnie liczone we wszystkich scenariuszach dla implementacji platformy .NET innych niż .NET 5+.
Aby uzyskać więcej informacji, zobacz Grapheme clusters (Klastry Grapheme).
Jak utworzyć wystąpienie obiektu Rune
Istnieje kilka sposobów uzyskiwania Rune
wystąpienia. Konstruktora można użyć do utworzenia obiektu Rune
bezpośrednio z:
Punkt kodu.
Rune a = new Rune(0x0061); // LATIN SMALL LETTER A Rune b = new Rune(0x10421); // DESERET CAPITAL LETTER ER
char
Pojedynczy element .Rune c = new Rune('a');
Para zastępcza
char
.Rune d = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL
Wszystkie konstruktory zgłaszają wartość ArgumentException
, jeśli dane wejściowe nie reprezentują prawidłowej wartości skalarnej Unicode.
Rune.TryCreate Istnieją metody dostępne dla osób wywołujących, którzy nie chcą zgłaszać wyjątków w przypadku awarii.
Rune
wystąpienia mogą być również odczytywane z istniejących sekwencji wejściowych. Na przykład, biorąc pod uwagę ReadOnlySpan<char>
, że reprezentuje dane UTF-16, Rune.DecodeFromUtf16 metoda zwraca pierwsze Rune
wystąpienie na początku zakresu danych wejściowych. Metoda Rune.DecodeFromUtf8 działa podobnie, akceptując ReadOnlySpan<byte>
parametr reprezentujący dane UTF-8. Istnieją równoważne metody odczytywania od końca zakresu zamiast początku zakresu.
Właściwości kwerendy obiektu Rune
Aby uzyskać wartość punktu kodu liczby całkowitej Rune
wystąpienia, użyj Rune.Value właściwości .
Rune rune = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL
int codePoint = rune.Value; // = 128302 decimal (= 0x1F52E)
Wiele statycznych interfejsów API dostępnych w typie char
jest również dostępnych w typie Rune
. Na przykład Rune.IsWhiteSpace metody i Rune.GetUnicodeCategory są równoważne Char.IsWhiteSpace metodom i .Char.GetUnicodeCategory Metody Rune
poprawnie obsługują pary zastępcze.
Poniższy przykładowy kod przyjmuje ReadOnlySpan<char>
jako dane wejściowe i przycina zarówno od początku, jak i na końcu zakresu, który Rune
nie jest literą ani cyfrą.
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;
}
Istnieją pewne różnice między interfejsem API a char
Rune
. Na przykład:
- Nie ma
Rune
odpowiednika Char.IsSurrogate(Char)elementu , ponieważRune
wystąpienia według definicji nigdy nie mogą być zastępczymi punktami kodu. - Funkcja Rune.GetUnicodeCategory nie zawsze zwraca ten sam wynik co Char.GetUnicodeCategory. Zwraca tę samą wartość co CharUnicodeInfo.GetUnicodeCategory. Aby uzyskać więcej informacji, zobacz uwagi na temat Char.GetUnicodeCategory.
Konwertowanie klasy Rune
na utF-8 lub UTF-16
Rune
Ponieważ element jest wartością skalarną Unicode, można go przekonwertować na kodowanie UTF-8, UTF-16 lub UTF-32. Typ Rune
ma wbudowaną obsługę konwersji na utF-8 i UTF-16.
Element Rune.EncodeToUtf16 konwertuje Rune
wystąpienie na char
wystąpienia. Aby zbadać liczbę char
wystąpień, które mogłyby wynikać z konwersji Rune
wystąpienia na UTF-16, użyj Rune.Utf16SequenceLength właściwości . Podobne metody istnieją dla konwersji UTF-8.
Poniższy przykład konwertuje Rune
wystąpienie na tablicę char
. W kodzie przyjęto założenie, Rune
że masz wystąpienie w zmiennej rune
:
char[] chars = new char[rune.Utf16SequenceLength];
int numCharsWritten = rune.EncodeToUtf16(chars);
Ponieważ element jest string
sekwencją znaków UTF-16, poniższy przykład konwertuje Rune
również wystąpienie na utF-16:
string theString = rune.ToString();
Poniższy przykład konwertuje Rune
wystąpienie na tablicę bajtów UTF-8
:
byte[] bytes = new byte[rune.Utf8SequenceLength];
int numBytesWritten = rune.EncodeToUtf8(bytes);
Metody Rune.EncodeToUtf16 i Rune.EncodeToUtf8 zwracają rzeczywistą liczbę zapisanych elementów. Zgłaszają wyjątek, jeśli bufor docelowy jest zbyt krótki, aby zawierać wynik. Istnieją metody bez zgłaszania TryEncodeToUtf8 , TryEncodeToUtf16 a także dla osób wywołujących, którzy chcą uniknąć wyjątków.
Uruchamianie na platformie .NET a w innych językach
Termin "rune" nie jest zdefiniowany w standardzie Unicode. Termin sięga tworzenia utF-8. Rob Pike i Ken Thompson szukali terminu, aby opisać, co ostatecznie stanie się znane jako punkt kodu. Osiedlili się na termin "rune", a później wpływ Roba Pike'a na język programowania Go pomógł popularyzować termin.
Jednak typ platformy .NET Rune
nie jest odpowiednikiem typu Go rune
. W języku rune
Go typ jest aliasem dla elementu int32
. Element Rune języka Go ma reprezentować punkt kodu Unicode, ale może to być dowolna wartość 32-bitowa, w tym zastępcze punkty kodu i wartości, które nie są legalnymi punktami kodu Unicode.
Aby zapoznać się z podobnymi typami w innych językach programowania, zobacz Typ pierwotny char
Rust lub typ swiftUnicode.Scalar
, z których oba reprezentują wartości skalarne Unicode. Zapewniają one funkcje podobne do . Typ platformy Rune
NET i nie zezwalają na tworzenie wystąpień wartości, które nie są legalnymi wartościami skalarnymi Unicode.