Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Poznámka:
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á libovolný 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, kódový bod, náhradní rozmezí 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
Zvažte použití typu Rune, pokud váš kód:
- Volá rozhraní API, která vyžadují skalární hodnoty Unicode.
- Explicitní zpracování náhradních dvojic
Rozhraní API, která vyžadují skalární hodnoty Unicode
Pokud váš kód prochází char instancemi v string nebo ReadOnlySpan<char>, některé z char metod nebudou správně fungovat na char instancích, které jsou v surrogate range. 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 na to, aby se používal Rune místo char, metoda funguje správně s kódovými body 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 se nepoužívá Rune
Pokud váš kód splňuje podmínky, nemusíte používat typ Rune.
- Hledá přesné
charshody. - Rozdělí řetězec podle zadaného znaku.
Použití typu Rune 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ěl řetězec 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 Graphemové clustery.
Jak vytvořit instanci 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 ERJeden
char.Rune c = new Rune('a');Náhradní
charpár.Rune d = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL
Všechny konstruktory vyvolají výjimku ArgumentException, pokud vstup nepředstavuje platnou skalární hodnotu Unicode.
Pro volající, kteří nechtějí, aby při selhání docházelo k vyvolání výjimek, jsou dostupné metody Rune.TryCreate.
Rune Instance lze také číst z existujících vstupních sekvencí. Například pokud ReadOnlySpan<char> představuje data UTF-16, metoda Rune.DecodeFromUtf16 vrátí první výskyt Rune na začátku vstupního úseku. Metoda Rune.DecodeFromUtf8 funguje podobně a přijímá ReadOnlySpan<byte> parametr, který představuje data UTF-8. Jsou k dispozici ekvivalentní metody pro čtení od konce rozsahu místo od 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 typ char je také k dispozici pro typ Rune. Například jsou Rune.IsWhiteSpace a Rune.GetUnicodeCategory ekvivalenty k metodám Char.IsWhiteSpace a Char.GetUnicodeCategory. 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 char a Rune existují určité rozdíly v API. Například:
- Neexistuje žádný
Runeekvivalent , Char.IsSurrogate(Char)protožeRuneinstance 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řeveďte Rune na 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.EncodeToUtf16 Převede Rune instanci na char instance. Chcete-li zjistit počet instancí char, které by byly výsledkem převodu instance Rune na UTF-16, použijte vlastnost Rune.Utf16SequenceLength. 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 instanci Rune v proměnné rune.
char[] chars = new char[rune.Utf16SequenceLength];
int numCharsWritten = rune.EncodeToUtf16(chars);
Vzhledem k tomu, že string je posloupností znaků UTF-16, následující příklad také převede instanci Rune 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ů. Vyvolají výjimku, pokud je cílová vyrovnávací paměť příliš krátká, aby obsahovala výsledek. Existují i metody TryEncodeToUtf8 a TryEncodeToUtf16 pro volající, kteří se chtějí vyhnout výjimkám.
Runy v .NET vs. 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. Dohodli se na označení "rune", a později vliv Roba Pika na programovací jazyk Go pomohl tento termín zpopularizovat.
Typ .NET Rune však není ekvivalentem typu Go rune . V Go je rune typ alias pro int32. Runa Go má představovat kódový bod Unicode, ale může to být jakákoli 32bitová hodnota, včetně náhradních kódových bodů a hodnot, které nejsou legálními kódovými body Unicode.
Podobné typy v jiných programovacích jazycích najdete, viz primitivní typ char Rustu nebo typ Unicode.Scalar Swiftu, které oba představují skalární hodnoty Unicode. Poskytují funkce podobné typu Rune v .NET a nepovolují vytváření instancí hodnot, které nejsou platnými skalárními hodnotami Unicode.