System.Text.Rune 結構
本文提供此 API 參考文件的補充備註。
Rune實例代表 Unicode 純量值,這表示排除代理範圍的任何程式代碼點 (U+D800..U+DFFF)。 型別的建構函式和轉換運算子會驗證輸入,因此取用者可以呼叫 API,假設基礎 Rune 實例的格式良好。
如果您不熟悉 Unicode 純量值、程式代碼點、代理範圍和格式正確的詞彙,請參閱 .NET 中的字元編碼簡介。
使用 Rune 類型的時機
如果您的程式代碼, Rune
請考慮使用 類型:
- 呼叫需要 Unicode 純量值的 API
- 明確處理代理字組
需要 Unicode 純量值的 API
如果您的程式代碼char
逐一查看 或ReadOnlySpan<char>
中的string
實例,某些char
方法將無法在代理範圍中的實例上正確char
運作。 例如,下列 API 需要純量值 char
才能正常運作:
- Char.GetNumericValue
- Char.GetUnicodeCategory
- Char.IsDigit
- Char.IsLetter
- Char.IsLetterOrDigit
- Char.IsLower
- Char.IsNumber
- Char.IsPunctuation
- Char.IsSymbol
- Char.IsUpper
下列範例顯示如果任一實例是代理程式代碼點, char
將無法正確運作的程式代碼:
// 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
以下是適用於 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;
}
上述程式代碼適用於某些語言,例如英文:
CountLettersInString("Hello")
// Returns 5
但它不適用於基本多語平面以外的語言,例如 Osage:
CountLettersInString("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟")
// Returns 0
此方法傳回 Osage 文字 char
不正確結果的原因是 Osage 字母的實例是 Surrogate 字碼點。 沒有單一 Surrogate 字碼點有足夠的信息來判斷其是否為字母。
如果您將此程式碼變更為使用 Rune
, char
而不是 ,此方法會正確搭配基本多語平面以外的程式代碼點運作:
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
以下是適用於 ReadOnlySpan<char>
的對等程式代碼:
static int CountLetters(ReadOnlySpan<char> span)
{
int letterCount = 0;
foreach (Rune rune in span.EnumerateRunes())
{
if (Rune.IsLetter(rune))
{ letterCount++; }
}
return letterCount;
}
上述程式代碼會正確計算 Osage 字母:
CountLettersInString("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟")
// Returns 8
明確處理代理字組的程序代碼
如果您的程式代碼呼叫明確在代理程式代碼點上操作的 API,請考慮使用 Rune
類型,例如下列方法:
- Char.IsSurrogate
- Char.IsSurrogatePair
- Char.IsHighSurrogate
- Char.IsLowSurrogate
- Char.ConvertFromUtf32
- Char.ConvertToUtf32
例如,下列方法具有處理 Surrogate 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.");
}
}
}
如果這類程式代碼使用 Rune
,則比較簡單,如下列範例所示:
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
的時機
如果您的程式代碼, 您不需要使用 Rune
類型:
- 尋找完全
char
相符專案 - 在已知的 char 值上分割字串
如果您的程式 Rune
代碼, 使用 類型可能會傳回不正確的結果:
- 計算中的顯示字元數目
string
尋找完全 char
相符專案
下列程式代碼會逐一 string
查看尋找特定字元,並傳回第一個相符專案的索引。 不需要變更此程式代碼來使用 Rune
,因為程式代碼正在尋找以單 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
}
在已知的上分割字串 char
呼叫和使用分隔符很常見 string.Split
,例如 ' '
(空格) 或 ','
(逗號),如下列範例所示:
string inputString = "🐂, 🐄, 🐆";
string[] splitOnSpace = inputString.Split(' ');
string[] splitOnComma = inputString.Split(',');
這裡不需要使用 Rune
,因為程式代碼正在尋找以單 char
一 表示的字元。
計算中的顯示字元數目 string
字串中的實例數目 Rune
可能不符合顯示字串時所顯示的使用者感知字元數目。
由於 Rune
實例代表 Unicode 純量值,因此遵循 Unicode 文字分割指導方針的 元件可用來 Rune
做為計算顯示字元的建置元件。
此 StringInfo 類型可用來計算顯示字元,但在 .NET 5+ 以外的所有案例中,它不會正確計算。
如需詳細資訊,請參閱 Grapheme 叢集。
如何具現化 Rune
有數種方式可以取得 Rune
實例。 您可以使用建構函式直接從下列專案建立 Rune
:
程式代碼點。
Rune a = new Rune(0x0061); // LATIN SMALL LETTER A Rune b = new Rune(0x10421); // DESERET CAPITAL LETTER ER
單一
char
。Rune c = new Rune('a');
代理
char
字組。Rune d = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL
如果輸入不代表有效的 Unicode 純量值,則所有建構函式都會擲回 ArgumentException
。
呼叫端有一個 Rune.TryCreate 方法,他們不希望在失敗時擲回例外狀況。
Rune
實例也可以從現有的輸入序列讀取。 例如,假設有 ReadOnlySpan<char>
代表UTF-16數據的 ,此方法 Rune.DecodeFromUtf16 會在輸入範圍開頭傳回第一個 Rune
實例。 方法 Rune.DecodeFromUtf8 的運作方式類似,接受 ReadOnlySpan<byte>
代表UTF-8數據的參數。 有對等的方法可從範圍結尾讀取,而不是範圍開頭。
的查詢屬性 Rune
若要取得 實例的 Rune
整數代碼點值,請使用 Rune.Value 屬性。
Rune rune = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL
int codePoint = rune.Value; // = 128302 decimal (= 0x1F52E)
類型上 char
可用的許多靜態 API 也可用於 Rune
類型。 例如, Rune.IsWhiteSpace 和 Rune.GetUnicodeCategory 相當於 Char.IsWhiteSpace 和 Char.GetUnicodeCategory 方法。 Rune
方法可正確處理 Surrogate 字組。
下列範例程式代碼會接受 ReadOnlySpan<char>
做為輸入,並從範圍開頭和結尾 Rune
修剪每個不是字母或數字的結尾。
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;
}
和 Rune
之間char
有一些 API 差異。 例如:
- 沒有
Rune
與 相等的 Char.IsSurrogate(Char),因為Rune
根據定義實例永遠不能是代理程式代碼點。 - Rune.GetUnicodeCategory不一定會傳回與 Char.GetUnicodeCategory相同的結果。 它會傳回與 CharUnicodeInfo.GetUnicodeCategory相同的值。 如需詳細資訊,請參閱 上的Char.GetUnicodeCategory。
Rune
將轉換為UTF-8或UTF-16
Rune
由於 是 Unicode 純量值,因此可以轉換成 UTF-8、UTF-16 或 UTF-32 編碼。 此 Rune
類型內建支持轉換成UTF-8和UTF-16。
會將 Rune.EncodeToUtf16Rune
實例 char
轉換成 實例。 若要查詢將實例轉換成 Rune
UTF-16所產生的實例數目char
,請使用 Rune.Utf16SequenceLength 屬性。 UTF-8 轉換也有類似的方法。
下列範例會將 Rune
實例 char
轉換成陣列。 程式代碼假設您在變數中有 rune
實例Rune
:
char[] chars = new char[rune.Utf16SequenceLength];
int numCharsWritten = rune.EncodeToUtf16(chars);
string
由於 是UTF-16字元序列,因此下列範例也會將 實例轉換成 Rune
UTF-16:
string theString = rune.ToString();
下列範例會將 Rune
實體 UTF-8
轉換成位元組數組:
byte[] bytes = new byte[rune.Utf8SequenceLength];
int numBytesWritten = rune.EncodeToUtf8(bytes);
Rune.EncodeToUtf16和 Rune.EncodeToUtf8 方法會傳回寫入之項目的實際數目。 如果目的地緩衝區太短而無法包含結果,它們就會擲回例外狀況。 對於想要避免例外狀況的呼叫端,也有非擲回 TryEncodeToUtf8 和 TryEncodeToUtf16 方法。
.NET 中的 Rune 與其他語言
“rune” 一詞未定義於 Unicode 標準中。 此詞彙可追溯到 UTF-8的建立。 羅布·派克和肯·湯普森正在尋找一個詞彙,以描述最終會被稱為代碼點的內容。 他們解決了“rune”一詞,羅布·派克後來對Go程式設計語言的影響有助於普及這個詞。
不過,.NET Rune
類型與 Go rune
類型不相等。 在 Go 中,類型 rune
是 的 int32
別名。 Go rune 的目的是要代表 Unicode 字碼點,但它可以是任何 32 位值,包括代理字碼點和不是合法 Unicode 字碼點的值。
如需其他程式設計語言中的類似類型,請參閱 Rust 的基本 char
類型 或 Swift Unicode.Scalar
的類型,這兩者都代表 Unicode 純量值。 它們提供類似的功能。NET 的類型 Rune
,且不允許具現化非合法 Unicode 純量值的值。