C# 字素
最后修改于 2025 年 5 月 24 日
在本文中,我们将使用 C# 中的字素。 字素是任何给定语言书写系统中最小的单位。
字素
字素是任何给定语言书写系统中最小的功能单位。 它本身可能不传达意义,并且不一定对应于口语中的单个音素。 字素可以由单个字符或多个组合元素组成,例如重音符号和变音符号,形成视觉上不同的符号。
从历史上看,术语字符是指原始 ASCII 表中的单个符号,该表仅支持有限的字符集。 但是,随着数字文本表示的扩展,开发了 Unicode 标准以适应书面语言的巨大多样性,从而允许支持复杂的脚本、表情符号和多字符字素。
Unicode
Unicode 是一项全球公认的计算行业标准,可确保跨各种书写系统对文本进行一致的编码、表示和处理。 它的开发是为了适应人类语言中使用的字符的巨大多样性,以及符号、表情符号和特殊文本元素。
在 C# 中,字符串是 Unicode 字符的序列,用作数据类型,将文本数据存储为一系列值,通常表示为字节。 字符串中的每个元素都对应于基于特定编码系统的字符。 C# 内部使用 UTF-16 编码,允许直接表示最常用的字符,同时支持扩展 Unicode 范围的代理对。
除了 ASCII 表之外,使用术语字素而不是“字符”更为准确。.NET 平台将文本元素定义为显示为字素的文本单元。 TextElementEnumerator 允许枚举字符串中的文本元素,其中可能包括基本字符、代理对或组合序列,这些组合序列共同形成单个显示的符号。
码位是指 Unicode 标准中分配给字符的数值。 例如,拉丁字母 Q 对应于 U+0051 码位,而西里尔小写字母 ж 由 U+0436 表示。 Unicode 为每个字符分配一个唯一的码位,确保文本处理中的全局兼容性。
字素簇是一个或多个码位的序列,它们组合起来形成一个图形单元。 例如,印地语音节 ते 由两个码位组成:U+0924 代表 त,U+0947 代表 े。 字素簇对于正确渲染复杂脚本至关重要。
字符串的实际存储表示形式由字节组成,字节对每个码位进行编码。 根据使用的 Unicode 标准(例如 UTF-8、UTF-16 或 UTF-32),每个码位可能需要不同数量的字节才能在内存或存储中完全表示。
Unicode 表
以下是 Unicode 表的一小部分,显示了码位、字符及其名称
| 码位 | 字符 | 名称 |
|---|---|---|
| U+0041 | A | LATIN CAPITAL LETTER A (拉丁大写字母 A) |
| U+0061 | a | LATIN SMALL LETTER A (拉丁小写字母 A) |
| U+03B1 | α | GREEK SMALL LETTER ALPHA (希腊小写字母 ALPHA) |
| U+0416 | Ж | CYRILLIC CAPITAL LETTER ZHE (西里尔大写字母 ZHE) |
| U+05D0 | א | HEBREW LETTER ALEF (希伯来字母 ALEF) |
| U+0924 | त | DEVANAGARI LETTER TA (梵文字母 TA) |
| U+1F600 | 😀 | GRINNING FACE (露齿而笑的脸) |
代理对
C# 利用 UTF-16 编码方案来存储 Unicode 文本,其中每个字符由 16 位(双字节)代码单元表示。 此编码完全支持 基本多文种平面 (BMP) 中的字符,该平面范围从 U+0000 到 U+FFFF。 BMP 包含最常用的字符,包括拉丁文、希腊文、西里尔文、阿拉伯文、希伯来文、中文、日文和韩文,以及数学符号和标点符号。
但是,某些 Unicode 字符会超出 BMP 的范围,占据 U+10000 到 U+10FFFF 的码位。 为了容纳这些字符,UTF-16 编码中使用了代理对。
代理对由两个 16 位代码单元组成,它们一起编码 BMP 之外的单个 Unicode 字符。 第一个代码单元称为高位代理,而第二个代码单元是低位代理。 这些对使 UTF-16 能够完全支持扩展的 Unicode 字符,包括表情符号、罕见的历史脚本以及各种领域中使用的专用符号。
此外,Unicode 定义了一个组合字符序列,该序列由一个基本字符和一个或多个修改它的组合字符组成。 当与其它码位组合时,代理对可以表示基本字符,也可以是较大字素簇的一部分。
| 码位 | 字符 | 高位代理 | 低位代理 | 名称 |
|---|---|---|---|---|
| U+1F600 | 😀 | D83D | DE00 | GRINNING FACE (露齿而笑的脸) |
| U+1F680 | 🚀 | D83D | DE80 | ROCKET (火箭) |
| U+1F4A9 | 💩 | D83D | DCA9 | PILE OF POO (一堆便便) |
| U+1F60D | 😍 | D83D | DE0D | SMILING FACE WITH HEART-EYES (带有心眼的微笑表情) |
| U+1F47B | 👻 | D83D | DC7B | GHOST (鬼魂) |
在上表中,码位 U+1F600、U+1F680、U+1F4A9、U+1F60D 和 U+1F47B 表示为代理对。 高位代理是第一个代码单元 (D83D),低位代理是第二个代码单元 (DE00, DE80, DCA9, DE0D, DC7B)。 这些对允许表示基本多文种平面 (BMP) 之外的字符,其中包括许多表情符号和其他特殊字符。
C# 字素示例
在以下示例中,我们将使用字素。
using System.Text;
using System.Globalization;
Console.OutputEncoding = System.Text.Encoding.UTF8;
Console.WriteLine("The Hindi word Namaste");
string word = "नमस्ते";
Console.WriteLine(word);
Console.WriteLine();
// code points
Console.WriteLine("Code points:");
for (int i = 0; i < word.Length; i += Char.IsSurrogatePair(word, i) ? 2 : 1)
{
int x = Char.ConvertToUtf32(word, i);
Console.WriteLine("U+{0:X4} {1}", x, Char.ConvertFromUtf32(x));
}
Console.WriteLine();
// bytes
Console.WriteLine("Bytes: ");
byte[] bytes = Encoding.UTF8.GetBytes(word);
foreach (byte c in bytes)
{
Console.Write($"{c} ");
}
Console.WriteLine("\n");
// graphemes
Console.WriteLine("Graphemes: ");
int count = 0;
TextElementEnumerator graphemeEnum = StringInfo.GetTextElementEnumerator(word);
while (graphemeEnum.MoveNext())
{
string grapheme = graphemeEnum.GetTextElement();
Console.WriteLine(grapheme);
count++;
}
Console.WriteLine($"the word has {count} graphemes");
该示例定义了一个变量,其中包含印地语单词 namaste。 我们打印该单词,打印其码位、字节,并打印和计算字素的数量。
Console.OutputEncoding = System.Text.Encoding.UTF8;
要将 Unicode 字符输出到终端,我们将控制台输出编码设置为 UTF8。
string word = "नमस्ते"; Console.WriteLine(word);
我们有印地语 namaste 单词; 我们将其打印到控制台。
// code points
Console.WriteLine("Code points:");
for (int i = 0; i < word.Length; i += Char.IsSurrogatePair(word, i) ? 2 : 1)
{
int x = Char.ConvertToUtf32(word, i);
Console.WriteLine("U+{0:X4} {1}", x, Char.ConvertFromUtf32(x));
}
我们打印单词的码位。 Lenth 属性确定字符串中 UTF-16 字符的数量。 Char.IsSurrogatePair 方法用于确定字符串中指定位置的两个相邻 Char 对象是否形成代理对。 如果是这样,我们需要更多字节来表示字素。
使用 Char.ConvertToUft32 方法,我们打印 Unicode 码位; 该方法将字符串中指定位置的 UTF-16 编码字符或代理对的值转换为 Unicode 码位。 最后,Char.ConvertFromUtf32 方法将给定的 Unicode 码位转换为 UTF-16 编码的字符串; 我们得到字素。
Console.WriteLine("Bytes: ");
byte[] bytes = Encoding.UTF8.GetBytes(word);
foreach (byte c in bytes)
{
Console.Write($"{c} ");
}
Console.WriteLine("\n");
我们打印存储在磁盘上的单词的实际字节。 我们使用 Encoding.UTF8.GetBytes 方法来获取底层字节数组。
Console.WriteLine("Graphemes: ");
int count = 0;
TextElementEnumerator graphemeEnum = StringInfo.GetTextElementEnumerator(word);
while (graphemeEnum.MoveNext())
{
string grapheme = graphemeEnum.GetTextElement();
Console.WriteLine(grapheme);
count++;
}
Console.WriteLine($"the word has {count} graphemes");
我们打印单词的字素并对其进行计数。 TextElementEnumerator 用于枚举单词的字素。 GetTextElement 用于获取当前文本元素(字素)。
$ dotnet run The Hindi word Namaste नमस्ते Code points: U+0928 न U+092E म U+0938 स U+094D्U+0924 त U+0947ेBytes: 224 164 168 224 164 174 224 164 184 224 165 141 224 164 164 224 165 135 Graphemes: न म स् ते the word has 4 graphemes
来源
使用 Unicode 的有用站点:www.fileformat.info 和 www.utf8-chartable.de。
在本文中,我们使用了 Unicode 字符串的字素、码位、字节和代理对。
作者
列出所有 C# 教程。