System.Text.Rune – struktura
Tento článek obsahuje doplňující poznámky k referenční dokumentaci pro toto rozhraní API.
Rune Instance představuje skalární hodnotu Unicode, což znamená, že jakýkoli bod kódu s výjimkou náhradního rozsahu (U+D800.). U+DFFF). Konstruktory a konverzní operátory typu ověřují vstup, takže uživatelé mohou volat rozhraní API za předpokladu, že je základní Rune instance správně vytvořená.
Pokud neznáte termíny skalární hodnota Unicode, bod kódu, náhradní oblast a dobře formátovaný, přečtěte si téma Úvod do kódování znaků v .NET.
Kdy použít typ Rune
Pokud kód používáte, Rune
zvažte použití typu:
- Volá rozhraní API, která vyžadují skalární hodnoty Unicode.
- Explicitní zpracování náhradních párů
Rozhraní API, která vyžadují skalární hodnoty Unicode
Pokud váš kód prochází char
instancemi v string
nebo a ReadOnlySpan<char>
, některé z char
metod nebudou správně fungovat na char
instancích, které jsou v náhradním rozsahu. Například následující rozhraní API vyžadují, aby skalární hodnota char
fungovala správně:
- Char.GetNumericValue
- Char.GetUnicodeCategory
- Char.IsDigit
- Char.IsLetter
- Char.IsLetterOrDigit
- Char.IsLower
- Char.IsNumber
- Char.IsPunctuation
- Char.IsSymbol
- Char.IsUpper
Následující příklad ukazuje kód, který nebude správně fungovat, pokud některé z char
instancí jsou náhradní body kódu:
// 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
Tady je ekvivalentní kód, který funguje s 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;
}
Předchozí kód funguje správně s některými jazyky, jako je angličtina:
CountLettersInString("Hello")
// Returns 5
Ale nebude fungovat správně pro jazyky mimo základní vícejazyčnou rovinu, například Osage:
CountLettersInString("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟")
// Returns 0
Důvodem, proč tato metoda vrací nesprávné výsledky pro text Osage je, že char
instance písmen Osage jsou náhradní body kódu. Žádný bod náhradního kódu nemá dostatek informací k určení, jestli se jedná o písmeno.
Pokud změníte tento kód tak, aby se místo toho používal Rune
char
, metoda funguje správně s body kódu mimo základní vícejazyčnou rovinu:
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
Tady je ekvivalentní kód, který funguje s ReadOnlySpan<char>
:
static int CountLetters(ReadOnlySpan<char> span)
{
int letterCount = 0;
foreach (Rune rune in span.EnumerateRunes())
{
if (Rune.IsLetter(rune))
{ letterCount++; }
}
return letterCount;
}
Předchozí kód počítá správně písmena Osage:
CountLettersInString("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟")
// Returns 8
Kód, který explicitně zpracovává náhradní páry
Zvažte použití Rune
typu, pokud váš kód volá rozhraní API, která explicitně pracují s náhradními body kódu, například následující metody:
- Char.IsSurrogate
- Char.IsSurrogatePair
- Char.IsHighSurrogate
- Char.IsLowSurrogate
- Char.ConvertFromUtf32
- Char.ConvertToUtf32
Například následující metoda má speciální logiku pro řešení náhradních char
pá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.");
}
}
}
Takový kód je jednodušší, pokud používá Rune
, jako v následujícím příkladu:
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
}
}
Kdy nepoužívat atribut Rune
Pokud kód používáte, nemusíte ho Rune
používat:
- Hledá přesné
char
shody. - Rozdělí řetězec na známou hodnotu znaku.
Rune
Použití typu může vrátit nesprávné výsledky, pokud váš kód:
- Spočítá počet zobrazovaných znaků v
string
Vyhledání přesných char
shod
Následující kód prochází hledáním string
konkrétních znaků a vrátí index první shody. Tento kód není nutné měnit tak, aby se používal Rune
, protože kód hledá znaky, které jsou reprezentovány jedním char
.
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
}
Rozdělení řetězce na známé char
Je běžné volat string.Split
a používat oddělovače, jako ' '
je (mezera) nebo ','
(čárka), jak je znázorněno v následujícím příkladu:
string inputString = "🐂, 🐄, 🐆";
string[] splitOnSpace = inputString.Split(' ');
string[] splitOnComma = inputString.Split(',');
Zde není nutné používat Rune
, protože kód hledá znaky, které jsou reprezentovány jedním char
.
Spočítat počet zobrazovaných znaků v string
Počet Rune
instancí v řetězci nemusí odpovídat počtu uživatelsky srozumitelných znaků zobrazených při zobrazení řetězce.
Vzhledem k tomu, že Rune
instance představují skalární hodnoty Unicode, mohou komponenty, které dodržují pokyny pro segmentaci textu Unicode, použít Rune
jako stavební blok pro počítání zobrazovaných znaků.
Typ StringInfo lze použít k počítání zobrazovaných znaků, ale ve všech scénářích jiných než .NET 5 nebo novějších se nepočítá správně.
Další informace najdete v tématu Clustery Grapheme.
Vytvoření instance instance Rune
Existuje několik způsobů, jak získat Rune
instanci. Konstruktor můžete použít k vytvoření Rune
přímo z:
Bod kódu.
Rune a = new Rune(0x0061); // LATIN SMALL LETTER A Rune b = new Rune(0x10421); // DESERET CAPITAL LETTER ER
Jeden .
char
Rune c = new Rune('a');
Náhradní
char
pár.Rune d = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL
Všechny konstruktory vyvolá ArgumentException
výjimku, pokud vstup nepředstavuje platnou skalární hodnotu Unicode.
Rune.TryCreate Existují metody pro volající, kteří nechtějí, aby výjimky byly vyvolány při selhání.
Rune
Instance lze také číst z existujících vstupních sekvencí. Například vzhledem k ReadOnlySpan<char>
tomu, že představuje data UTF-16, Rune.DecodeFromUtf16 metoda vrátí první Rune
instanci na začátku vstupního rozsahu. Metoda Rune.DecodeFromUtf8 funguje podobně a přijímá ReadOnlySpan<byte>
parametr, který představuje data UTF-8. Existují ekvivalentní metody pro čtení z konce rozsahu místo začátku rozsahu.
Vlastnosti dotazu Rune
Chcete-li získat celočíselnou hodnotu Rune
bodu kódu instance, použijte Rune.Value vlastnost.
Rune rune = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL
int codePoint = rune.Value; // = 128302 decimal (= 0x1F52E)
Mnoho statických rozhraní API dostupných pro char
daný typ je také k dispozici pro daný Rune
typ. Jsou to například Rune.IsWhiteSpaceRune.GetUnicodeCategory ekvivalenty Char.IsWhiteSpace a Char.GetUnicodeCategory metody. Metody Rune
správně zpracovávají náhradní páry.
Následující příklad kódu přebírá ReadOnlySpan<char>
jako vstup a ořízne z počátečního i koncového rozsahu každý Rune
, který není písmenem nebo číslicí.
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;
}
Mezi rozhraním Rune
API char
a . Příklad:
- Neexistuje žádný
Rune
ekvivalent , Char.IsSurrogate(Char)protožeRune
instance podle definice nemohou být nikdy náhradními body kódu. - Vždy Rune.GetUnicodeCategory nevrací stejný výsledek jako Char.GetUnicodeCategory. Vrátí stejnou hodnotu jako CharUnicodeInfo.GetUnicodeCategory. Další informace naleznete v poznámkách o Char.GetUnicodeCategory.
Převod na Rune
UTF-8 nebo UTF-16
Rune
Vzhledem k tomu, že je skalární hodnota Unicode, lze ji převést na kódování UTF-8, UTF-16 nebo UTF-32. Typ Rune
má integrovanou podporu převodu na UTF-8 a UTF-16.
Rune
Převede Rune.EncodeToUtf16 instanci na char
instance. Chcete-li dotazovat počet char
instancí, které by výsledkem převodu Rune
instance na UTF-16, použijte Rune.Utf16SequenceLength vlastnost. Podobné metody existují pro převod UTF-8.
Následující příklad převede Rune
instanci na char
pole. Kód předpokládá, že máte Rune
v rune
proměnné instanci:
char[] chars = new char[rune.Utf16SequenceLength];
int numCharsWritten = rune.EncodeToUtf16(chars);
string
Vzhledem k tomu, že je posloupnost znaků UTF-16, následující příklad také převede Rune
instanci na UTF-16:
string theString = rune.ToString();
Následující příklad převede Rune
instanci na bajtové UTF-8
pole:
byte[] bytes = new byte[rune.Utf8SequenceLength];
int numBytesWritten = rune.EncodeToUtf8(bytes);
Metody Rune.EncodeToUtf16 a Rune.EncodeToUtf8 vrátí skutečný počet zapsaných prvků. Vyvolá výjimku, pokud je cílová vyrovnávací paměť příliš krátká, aby obsahovala výsledek. Volajícím, kteří se chtějí vyhnout výjimkám, existují i metody, které nevyvolávají TryEncodeToUtf8TryEncodeToUtf16 .
Spuštění v .NET a jiných jazycích
Termín rune není definován ve standardu Unicode. Termín pochází zpět k vytvoření UTF-8. Rob Pike a Ken Thompson hledali termín, který by nakonec popsal, co by se nakonec stalo známým jako kódový bod. Usadili se na termínu "rune", a Rob Pike později vliv na programovací jazyk Go pomohl popularizovat termín.
Typ .NET Rune
však není ekvivalentem typu Go rune
. V Go je rune
typ alias pro int32
. Spuštění Go má představovat bod kódu Unicode, ale může to být jakákoli 32bitová hodnota, včetně náhradních bodů kódu a hodnot, které nejsou právními body kódu Unicode.
Podobné typy v jiných programovacích kódech najdete v jiných programovacích jazycích, viz primitivní typ Rustu nebo SwiftůvUnicode.Scalar
typchar
Poskytují funkce podobné . Net typ Rune
a nepovolují vytváření instancí hodnot, které nejsou právními skalárními hodnotami Unicode.