Rune 结构

定义

表示 Unicode 标量值([ U+0000..U+D7FF ],含首尾值;或 [ U+E000..U+10FFFF ],含首尾值)。

public value class Rune : IComparable, IComparable<System::Text::Rune>, IEquatable<System::Text::Rune>
public value class Rune : IComparable, IComparable<System::Text::Rune>, IEquatable<System::Text::Rune>, ISpanFormattable
public value class Rune : IComparable<System::Text::Rune>, IEquatable<System::Text::Rune>
public readonly struct Rune : IComparable, IComparable<System.Text.Rune>, IEquatable<System.Text.Rune>
public readonly struct Rune : IComparable, IComparable<System.Text.Rune>, IEquatable<System.Text.Rune>, ISpanFormattable
public readonly struct Rune : IComparable<System.Text.Rune>, IEquatable<System.Text.Rune>
type Rune = struct
type Rune = struct
    interface ISpanFormattable
    interface IFormattable
type Rune = struct
    interface IFormattable
    interface ISpanFormattable
Public Structure Rune
Implements IComparable, IComparable(Of Rune), IEquatable(Of Rune)
Public Structure Rune
Implements IComparable, IComparable(Of Rune), IEquatable(Of Rune), ISpanFormattable
Public Structure Rune
Implements IComparable(Of Rune), IEquatable(Of Rune)
继承
实现

注解

实例 Rune 表示 Unicode 标量值,表示排除代理项范围 (U+D800 的任何代码点。U+DFFF) 。 类型的构造函数和转换运算符验证输入,因此使用者可以调用 API,前提是基础 Rune 实例已形成良好。

如果不熟悉术语 Unicode 标量值、代码点、代理项范围和格式正确的术语,请参阅 .NET 中的字符编码简介

以下部分介绍:

何时使用 Rune 类型

Rune如果代码:

  • 调用需要 Unicode 标量值的 API
  • 显式处理代理项对

需要 Unicode 标量值的 API

如果代码循环访问 char 某个 string 或 a 中的 ReadOnlySpan<char>实例,则某些 char 方法将无法在代理项范围内的实例上 char 正常工作。 例如,以下 API 要求标量值 char 正常工作:

以下示例显示了如果任一 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 字母的实例是代理码位。 没有单个代理码点有足够的信息来确定它是一个字母。

如果更改此代码以使用 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 对的特殊逻辑:

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 匹配项
  • 拆分已知字符值上的字符串

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+ 以外的 .NET 实现的所有方案中,该类型无法正确计数。

有关详细信息,请参阅 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 数据的参数。 有等效的方法可从范围末尾读取,而不是从范围开头读取。

查询 a 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.GetUnicodeCategoryChar.IsWhiteSpaceChar.GetUnicodeCategory 方法。 方法 Rune 正确处理代理项对。

以下示例代码采用一个 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;
}

之间存在一些 API 差异char``Rune。 例如:

Rune转换为 UTF-8 或 UTF-16

由于 a Rune 是 Unicode 标量值,因此可以转换为 UTF-8、UTF-16 或 UTF-32 编码。 该 Rune 类型内置支持转换为 UTF-8 和 UTF-16。

Rune.EncodeToUtf16实例char转换为Rune实例。 若要查询将实例转换为 Rune UTF-16 而生成的实例数char,请使用该Rune.Utf16SequenceLength属性。 UTF-8 转换存在类似的方法。

以下示例将 Rune 实例转换为 char 数组。 该代码假定变量中有rune一个Rune实例:

char[] chars = new char[rune.Utf16SequenceLength];
int numCharsWritten = rune.EncodeToUtf16(chars);

由于 a 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.EncodeToUtf16Rune.EncodeToUtf8方法返回写入的实际元素数。 如果目标缓冲区太短而无法包含结果,则引发异常。 对于想要避免异常的调用方,也存在非引发 TryEncodeToUtf8EncodeToUtf16 方法。

.NET 中的 Rune 与其他语言

Unicode Standard 中未定义术语“rune”。 该术语可追溯到 UTF-8 的创建。 Rob Pike 和 Ken Thompson 正在寻找一个术语来描述最终被称为代码点的内容。 他们解决了术语“rune”,罗布·派克后来对 Go 编程语言的影响帮助普及了这个词。

但是,.NET Rune 类型与 Go rune 类型不相等。 在 Go 中,类型 rune别名 int32。 Go rune 旨在表示 Unicode 代码点,但它可以是任何 32 位值,包括代理代码点和不是合法 Unicode 码位的值。

有关其他编程语言中的类似类型,请参阅 Rust 的基元 char 类型Swift Unicode.Scalar 的类型,这两种类型都表示 Unicode 标量值。 它们提供的功能类似于 。NET 的类型 Rune ,它们禁止实例化不是合法的 Unicode 标量值的值。

构造函数

Rune(Char)

从提供的 UTF-16 代码单元中创建 Rune

Rune(Char, Char)

从提供的 UTF-16 代理项对中创建 Rune

Rune(Int32)

从表示 Unicode 标量值的指定 32 位整数中创建 Rune

Rune(UInt32)

从表示 Unicode 标量值的指定 32 位无符号整数中创建 Rune

属性

IsAscii

获取一个值,该值指示与此 Rune 关联的标量值是否在 ASCII 编码范围内。

IsBmp

获取一个值,该值指示与此 Rune 关联的标量值是否在 BMP 编码范围内。

Plane

获取包含此标量的 Unicode 平面(0 至 16,含 0 和 16)。

ReplacementChar

获取表示 Unicode 替换字符 U+FFFD 的 Rune 实例。

Utf16SequenceLength

获取表示此标量值所需的 UTF-16 序列的代码单元 (Char) 中的长度。

Utf8SequenceLength

获取表示此标量值所需的 UTF-8 序列的代码单元中的长度。

Value

获取整数形式的 Unicode 标量值。

方法

CompareTo(Rune)

将当前实例与指定的 Rune 实例进行比较。

DecodeFromUtf16(ReadOnlySpan<Char>, Rune, Int32)

在提供的 UTF-16 源缓冲区开始处解码 Rune

DecodeFromUtf8(ReadOnlySpan<Byte>, Rune, Int32)

在提供的 UTF-8 源缓冲区开始处解码 Rune

DecodeLastFromUtf16(ReadOnlySpan<Char>, Rune, Int32)

在提供的 UTF-16 源缓冲区结尾处解码 Rune

DecodeLastFromUtf8(ReadOnlySpan<Byte>, Rune, Int32)

在提供的 UTF-8 源缓冲区结尾处解码 Rune

EncodeToUtf16(Span<Char>)

将此 Rune 解码为 UTF-16 目标缓冲区。

EncodeToUtf8(Span<Byte>)

将此 Rune 解码为 UTF-8 目标缓冲区。

Equals(Object)

返回一个指示当前实例是否与指定对象相等的值。

Equals(Rune)

返回一个值,该值指示当前实例是否与指定的 rune 相等。

GetHashCode()

返回此实例的哈希代码。

GetNumericValue(Rune)

获取与指定 rune 关联的数值。

GetRuneAt(String, Int32)

获取在字符串中指定位置开始的 Rune

GetUnicodeCategory(Rune)

获取与指定 rune 关联的 Unicode 类别。

IsControl(Rune)

返回一个值,该值指示指定的 rune 是否属于控制字符类别。

IsDigit(Rune)

返回一个值,该值指示指定的 rune 是否属于十进制数字类别。

IsLetter(Rune)

返回一个值,该值指示指定的 rune 是否属于字母类别。

IsLetterOrDigit(Rune)

返回一个值,该值指示指定的 rune 属于字母类别还是十进制数字类别。

IsLower(Rune)

返回一个值,该值指示指定的 rune 是否属于小写字母类别。

IsNumber(Rune)

返回一个值,该值指示指定的 rune 是否属于数字类别。

IsPunctuation(Rune)

返回一个值,该值指示指定的 rune 是否属于标点符号类别。

IsSeparator(Rune)

返回一个值,该值指示指定的 rune 是否属于分隔符类别。

IsSymbol(Rune)

返回一个值,该值指示指定的 rune 是否属于符号字符类别。

IsUpper(Rune)

返回一个值,该值指示指定的 rune 是否属于大写字母类别。

IsValid(Int32)

返回一个值,该值指示 32 位带符号整数是否表示有效的 Unicode 标量值;即它在 [ U+0000..U+D7FF ](含首尾值)或 [ U+E000..U+10FFFF ](含首尾值)范围内。

IsValid(UInt32)

返回一个值,该值指示 32 位无符号整数是否表示有效的 Unicode 标量值;即它在 [ U+0000..U+D7FF ](含首尾值)或 [ U+E000..U+10FFFF ](含首尾值)范围内。

IsWhiteSpace(Rune)

返回一个值,该值指示指定的 rune 是否属于空格字符类别。

ToLower(Rune, CultureInfo)

根据指定区域性的大小写规则返回指定 Rune 转换为小写形式的副本。

ToLowerInvariant(Rune)

根据固定区域性的大小写规则返回指定 Rune 转换为小写形式的副本。

ToString()

返回此 Rune 实例的字符串表示形式。

ToUpper(Rune, CultureInfo)

根据指定区域性的大小写规则返回指定 Rune 转换为大写形式的副本。

ToUpperInvariant(Rune)

根据固定区域性的大小写规则返回指定 Rune 转换为大写形式的副本。

TryCreate(Char, Char, Rune)

尝试从指定的 UTF-16 代理项对中创建 Rune,并返回指示操作是否成功的值。

TryCreate(Char, Rune)

尝试从指定的字符中创建 Rune,并返回指示操作是否成功的值。

TryCreate(Int32, Rune)

尝试从表示 Unicode 标量值的指定带符号整数中创建 Rune

TryCreate(UInt32, Rune)

尝试从表示 Unicode 标量值的指定 32 位无符号整数中创建 Rune

TryEncodeToUtf16(Span<Char>, Int32)

将此 Rune 解码为 UTF-16 编码目标缓冲区。

TryEncodeToUtf8(Span<Byte>, Int32)

将此 Rune 解码为 UTF-8 编码目标缓冲区。

TryGetRuneAt(String, Int32, Rune)

尝试获取在字符串的指定位置处开始的 Rune,并返回指示操作是否成功的值。

运算符

Equality(Rune, Rune)

返回一个值,该值指示两个 Rune 实例是否相等。

Explicit(Char to Rune)

定义从 16 位 Unicode 字符到 Rune 的显式转换。

Explicit(Int32 to Rune)

定义从 32 位带符号整数到 Rune 的显式转换。

Explicit(UInt32 to Rune)

定义从 32 位无符号整数到 Rune 的显式转换。

GreaterThan(Rune, Rune)

返回一个值,该值指示指定的 Rune 是否大于另一个指定的 Rune

GreaterThanOrEqual(Rune, Rune)

返回一个值,该值指示指定的 Rune 是否大于等于另一个指定的 Rune

Inequality(Rune, Rune)

返回一个值,该值指示两个 Rune 实例是否具有不同的值。

LessThan(Rune, Rune)

返回一个值,该值指示指定的 Rune 是否小于另一个指定的 Rune

LessThanOrEqual(Rune, Rune)

返回一个值,该值指示指定的 Rune 是小于还是等于另一个指定的 Rune

显式接口实现

IComparable.CompareTo(Object)

将当前实例与指定的对象进行比较。

IFormattable.ToString(String, IFormatProvider)

使用指定格式对当前实例的值设置格式。

ISpanFormattable.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)

尝试将当前实例的值格式化为提供的字符范围。

适用于