Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
Den här artikeln innehåller ytterligare kommentarer till referensdokumentationen för det här API:et.
En Rune instans representerar ett Unicode-skalärt värde, vilket innebär alla kodpunkter utom surrogatintervallet (U+D800.. U+DFFF). Typens konstruktorer och konverteringsoperatorer validerar indata, så att användarna kan anropa API:erna förutsatt att den underliggande Rune instansen är väl utformad.
Om du inte är bekant med termerna Unicode scalar value, code point, surrogate range och well-formed läser du Introduktion till teckenkodning i .NET.
När du ska använda Rune-typen
Överväg att använda typen Rune om din kod:
- Anropar API:er som kräver Unicode-skalärvärden
- Hanterar uttryckligen surrogatpar
API:er som kräver Unicode-skalärvärden
Om koden itererar genom char instanserna i en string eller en ReadOnlySpan<char>fungerar vissa av char metoderna inte korrekt på char instanser som finns i surrogatintervallet. Följande API:er kräver till exempel att ett skalärt värde char fungerar korrekt:
- Char.GetNumericValue
- Char.GetUnicodeCategory
- Char.IsDigit
- Char.IsLetter
- Char.IsLetterOrDigit
- Char.IsLower
- Char.IsNumber
- Char.IsPunctuation
- Char.IsSymbol
- Char.IsUpper
I följande exempel visas kod som inte fungerar korrekt om någon av char instanserna är surrogatkodpunkter:
// 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
Här är motsvarande kod som fungerar med en 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;
}
Föregående kod fungerar korrekt med vissa språk, till exempel engelska:
CountLettersInString("Hello")
// Returns 5
Men det fungerar inte korrekt för språk utanför det grundläggande multipla språkområdet, såsom Osage:
CountLettersInString("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟")
// Returns 0
Anledningen till att den här metoden returnerar felaktiga resultat för Osage-text är att char instanserna för Osage-bokstäver är surrogatkodpunkter. Ingen enskild surrogatkodpunkt har tillräckligt med information för att avgöra om det är en bokstav.
Om du ändrar den här koden så att den används Rune i stället för charfungerar metoden korrekt med kodpunkter utanför det grundläggande flerspråkiga planet:
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
Här är motsvarande kod som fungerar med en ReadOnlySpan<char>:
static int CountLetters(ReadOnlySpan<char> span)
{
int letterCount = 0;
foreach (Rune rune in span.EnumerateRunes())
{
if (Rune.IsLetter(rune))
{ letterCount++; }
}
return letterCount;
}
Föregående kod räknar Osage-bokstäver korrekt:
CountLettersInString("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟")
// Returns 8
Kod som uttryckligen hanterar surrogatpar
Överväg att använda typen Rune om koden anropar API:er som uttryckligen används på surrogatkodpunkter, till exempel följande metoder:
- Char.IsSurrogate
- Char.IsSurrogatePair
- Char.IsHighSurrogate
- Char.IsLowSurrogate
- Char.ConvertFromUtf32
- Char.ConvertToUtf32
Följande metod har till exempel särskild logik för att hantera surrogatpar 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.");
}
}
}
Sådan kod är enklare om den använder Rune, som i följande exempel:
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
}
}
När du inte ska använda Rune
Du behöver inte använda typen Rune om koden:
- Söker efter exakta
charmatchningar - Delar upp en sträng på ett känt teckenvärde
Om du använder typen Rune kan det returnera felaktiga resultat om koden:
- Räknar antalet visningstecken i en
string
Leta efter exakta char matchningar
Följande kod itererar genom att string söka efter specifika tecken, vilket returnerar indexet för den första matchningen. Du behöver inte ändra koden för att använda Rune, eftersom koden letar efter tecken som representeras av en enda 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
}
Dela en sträng på en känd char
Det är vanligt att anropa string.Split och använda avgränsare som ' ' (blanksteg) eller ',' (kommatecken), som i följande exempel:
string inputString = "🐂, 🐄, 🐆";
string[] splitOnSpace = inputString.Split(' ');
string[] splitOnComma = inputString.Split(',');
Det finns inget behov av att använda Rune här, eftersom koden letar efter tecken som representeras av en enda char.
Räkna antalet visningstecken i en string
Antalet Rune instanser i en sträng kanske inte överensstämmer med antalet användarinställbara tecken som visas när strängen visas.
Eftersom Rune instanser representerar Unicode-skalärvärden kan komponenter som följer riktlinjerna för Unicode-textsegmentering användas Rune som byggblock för att räkna visningstecken.
Typen StringInfo kan användas för att räkna visningstecken, men den räknas inte korrekt i alla scenarier för andra .NET-implementeringar än .NET 5+.
Mer information finns i Grapheme-kluster.
Så här instansierar du en Rune
Det finns flera sätt att hämta en Rune instans. Du kan använda en konstruktor för att skapa en Rune direkt från:
En kodpunkt.
Rune a = new Rune(0x0061); // LATIN SMALL LETTER A Rune b = new Rune(0x10421); // DESERET CAPITAL LETTER EREn enda
char.Rune c = new Rune('a');Ett surrogatpar
char.Rune d = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL
Alla konstruktorer genererar ett ArgumentException om indata inte representerar ett giltigt Unicode-skalärvärde.
Det finns Rune.TryCreate metoder för anropare som inte vill att undantag ska genereras vid misslyckande.
Rune instanser kan också läsas från befintliga inmatningssekvenser. Till exempel, givet en ReadOnlySpan<char> som representerar UTF-16-data, returnerar metoden Rune.DecodeFromUtf16 den första instansen av Rune från början av indatasegmentet. Metoden Rune.DecodeFromUtf8 fungerar på samma sätt och accepterar en ReadOnlySpan<byte> parameter som representerar UTF-8-data. Det finns motsvarande metoder att läsa från slutet av intervallet i stället för början av intervallet.
Hämta egenskaper för en Rune
Använd Rune-egenskapen för att hämta heltalskodpunktsvärdet av en Rune.Value-instans.
Rune rune = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL
int codePoint = rune.Value; // = 128302 decimal (= 0x1F52E)
Många av de statiska API:er som är tillgängliga för char typen är också tillgängliga för Rune typen. Till exempel Rune.IsWhiteSpace och Rune.GetUnicodeCategory är motsvarigheter till Char.IsWhiteSpace och Char.GetUnicodeCategory metoder. Metoderna Rune hanterar surrogatpar korrekt.
Följande exempelkod tar en ReadOnlySpan<char> som indata och trimmar från både början och slutet av intervallet varje Rune som inte är en bokstav eller siffra.
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;
}
Det finns vissa API-skillnader mellan char och Rune. Till exempel:
- Det finns ingen
Runemotsvarighet till Char.IsSurrogate(Char), eftersomRuneinstanser per definition aldrig kan vara surrogatkodpunkter. - Rune.GetUnicodeCategory Returnerar inte alltid samma resultat som Char.GetUnicodeCategory. Det returnerar samma värde som CharUnicodeInfo.GetUnicodeCategory. Mer information finns i Anmärkningar på Char.GetUnicodeCategory.
Konvertera en Rune till UTF-8 eller UTF-16
Eftersom ett Rune är ett Unicode-skalärt värde kan det konverteras till UTF-8, UTF-16 eller UTF-32-kodning. Typen Rune har inbyggt stöd för konvertering till UTF-8 och UTF-16.
Konverterar Rune.EncodeToUtf16 en Rune instans till char instanser. För att fråga om antalet char instanser som skulle uppstå när en Rune instans konverteras till UTF-16 kan du använda Rune.Utf16SequenceLength egenskapen. Liknande metoder finns för UTF-8-konvertering.
I följande exempel konverteras en Rune instans till en char matris. Koden förutsätter att du har en Rune instans i variabeln rune :
char[] chars = new char[rune.Utf16SequenceLength];
int numCharsWritten = rune.EncodeToUtf16(chars);
Eftersom en string är en sekvens med UTF-16 tecken konverterar följande exempel även en Rune instans till UTF-16:
string theString = rune.ToString();
I följande exempel konverteras en Rune instans till en UTF-8 bytematris:
byte[] bytes = new byte[rune.Utf8SequenceLength];
int numBytesWritten = rune.EncodeToUtf8(bytes);
Metoderna Rune.EncodeToUtf16 och Rune.EncodeToUtf8 returnerar det faktiska antalet element som skrivits. De utlöser ett undantag om målbufferten är för kort för att innehålla resultatet. Det finns icke-kasta TryEncodeToUtf8 och TryEncodeToUtf16 metoder samt för anropare som vill undvika undantag.
Rune i .NET jämfört med andra språk
Termen "rune" definieras inte i Unicode Standard. Termen går tillbaka till skapandet av UTF-8. Rob Pike och Ken Thompson letade efter en term för att beskriva vad som så småningom skulle bli känt som en kodpunkt. De kom överens om termen "runa", och Rob Pikes senare inflytande över Go-programmeringsspråket hjälpte till att popularisera termen.
.NET-typen Rune motsvarar dock inte Go-typen rune . I Go är typen rune ett alias för int32. En Go-runa är avsedd att representera en Unicode-kodpunkt, men det kan vara valfritt 32-bitarsvärde, inklusive surrogatkodpunkter och värden som inte är juridiska Unicode-kodpunkter.
Liknande typer i andra programmeringsspråk finns i Rusts primitiva char typ eller Swifts Unicode.Scalar typ, som båda representerar Unicode-skalärvärden. De tillhandahåller funktioner som liknar . NET:s Rune typ och de tillåter inte instansiering av värden som inte är lagliga Unicode-skalärvärden.