(文本)字符串:字符串是 Unicode 字符的有序集合,用于表示文本。
Unicode 字符
Unicode编码: (抽象的)Unicode 字符(或叫文本字符):由码位标识(21 位标量数字)。
Unicode编码的实现:
UTF-16 编码格式:将码位编码为一个或多个 16 位值的序列,每个 16 位值的范围为十六进制的 0x0000 到 0xFFFF,并存储在 Char 结构中。
多数 (抽象的)Unicode 字符可由一个 Char 对象表示,但编码为基字符、代理项对和/或组合字符序列的字符由多个 Char 对象表示。
String 对象(字符串的实现):是 System.Char 对象的有序集合,用于表示字符串。
Char 对象:Char 对象的值是 16 位数字(序号)值。
代理项对和组合字符序列的 Unicode 支持
“Unicode 标准”将代理项对定义为由两个代码单元序列组成的单个抽象字符的编码字符表示形式。代理项对的第一个值是高代理项,它包含一个介于 U+D800 到 U+DBFF 之间的 16 位代码值。代理项对的第二个值是低代理项,它包含介于 U+DC00 到 U+DFFF 之间的值。通过使用代理项对,16 位 Unicode 编码系统可以对已由 Unicode 标准定义的一百多万个其他字符 (220) 进行寻址。代理项对扩展字符集之外 Unicode 字符。
“Unicode 标准”将组合字符序列定义为一个基字符与一个或多个组合字符的组合。代理项对可表示基字符或组合字符。有关代理项对和组合字符序列的更多信息,请参见位于 www.unicode.org 的 Unicode Standard(Unicode 标准)。
需要记住的关键一点是代理项对表示 32 位单个字符,不能假定一个 16 位 Unicode 编码值恰好映射为一个字符。通过使用代理项对,16 位 Unicode 编码系统可以表示另外一百万个代码数据点,而 Unicode 标准将为它们分配字符。
.NET Framework 支持文本元素。文本元素是显示为单字符的文本单元,称作字素。文本元素可以是基字符、代理项对或组合字符序列。StringInfo 类提供了一些方法,使您可以将字符串拆分为文本元素,并循环访问这些文本元素。例如,StringInfo.GetNextTextElement 方法允许将一个代理项对作为一个文本元素来检索。有关使用 StringInfo 类的示例,请参见字符串索引。
字符串是 Unicode 字符的有序集合,用于表示文本。(字符串的实现)String 对象是 System.Char 对象的有序集合,用于表示字符串。String 对象的值是该有序集合的内容,并且该值是不可变的。
String 对象称为不可变的(只读),因为一旦创建了该对象,就不能修改该对象的值。看来似乎修改了 String 对象的方法实际上是返回一个包含修改内容的新 String 对象。如果需要修改字符串对象的实际内容,请使用 System.Text.StringBuilder 类。
字符串中的每个 Unicode 字符都是由 Unicode 标量值定义的,Unicode 标量值也称为 Unicode 码位或者 Unicode 字符的序号(数字)值。每个码位都是使用 UTF-16 编码进行编码的,编码的每个元素的数值都用一个 Char 对象表示。
一个 Char 对象通常表示一个码位,即:Char 的数值等于该码位。但是,一个码位可能需要多个编码元素。例如,Unicode 辅助码位(代理项对)使用两个 Char 对象来编码。
索引
索引是 Char 对象在 String 中的位置,而不是 Unicode 字符的位置。索引是从零开始、从字符串的起始位置(其索引为零)计起的非负数字。连续的索引值可能并不与连续的 Unicode 字符相对应,这是因为一个 Unicode 字符可能会编码为多个 Char 对象。若要使用每个 Unicode 字符而不是每个 Char 对象,请使用 System.Globalization.StringInfo 类。
序号运算和区分区域性的运算
String 类的成员对 String 对象执行序号运算或语义运算。序号运算是对每个 Char 对象的数值执行的。语义运算则对考虑了特定于区域性的大小写、排序、格式化和语法分析规则的 String 的值执行。语义运算在显式声明的区域性或者隐式当前区域性的上下文中执行。有关当前区域性的更多信息,请参见 CultureInfo.CurrentCulture 主题。
大小写规则决定如何更改 Unicode 字符的大小写,例如,从小写变为大写。
格式化规则决定如何将值转换为它的字符串表示形式,而语法分析规则则确定如何将字符串表示形式转换为值。
排序规则确定 Unicode 字符的字母顺序,以及两个字符串如何互相比较。例如,Compare 方法执行语义比较,而 CompareOrdinal 方法执行序号比较。因此,如果当前的区域性为美国英语,则 Compare 方法认为“a”小于“A”,而 CompareOrdinal 方法会认为“a”大于“A”。
.NET Framework 支持单词、字符串和序号排序规则。单词排序会执行区分区域性的字符串比较,在这种比较中,某些非字母数字 Unicode 字符可能会具有特殊的权重。例如,连字符(“-”)的权重非常小,因此“coop”和“co-op”在排序列表中是紧挨着出现的。字符串排序与单词排序相似,只是所有非字母数字符号均排在所有字母数字 Unicode 字符前面,没有特例。
区分区域性的比较是显式或隐式使用 CultureInfo 对象的任何比较,包括由 CultureInfo.InvariantCulture 属性指定的固定区域性。当前隐式区域性由 Thread.CurrentCulture 属性指定。
序号排序基于字符串中每个 Char 对象的数值对字符串进行比较。序号比较自动区分大小写,因为字符的小写和大写版本有着不同的码位。但是,如果大小写在应用程序中并不重要,则可以指定忽略大小写的序号比较。这等效于使用固定区域性将字符串转换成大写,然后对结果执行序号比较。
有关单词、字符串和序号排序规则的更多信息,请参见 System.Globalization.CompareOptions 主题。
区分区域性的比较通常适用于排序,而序号比较则不适合。序号比较通常适用于确定两个字符串是否相等(即,确定标识),而区分区域性的比较则不适用。
比较和搜索方法的“备注”指定方法是区分大小写、区分区域性还是两者区分。根据定义,任何字符串(包括空字符串 (""))的比较结果都大于空引用;两个空引用的比较结果为相等。
规范化
某些 Unicode 字符具有多个等效的二进制表示形式,这些表示形式中包含几组组合的和/或复合的 Unicode 字符。Unicode 标准定义了一个称为规范化的过程,此过程将一个字符的任何一种等价二进制表示形式转换为统一的二进制表示形式。可使用多种遵循不同规则的算法执行规范化,这些算法也称为范式。.NET Framework 当前支持范式 C、D、KC 和 KD。通常用序号比较来评估一对规范化的字符串。
安全注意事项
如果应用程序进行有关符号标识符(如文件名或命名管道)或持久数据(如 XML 文件中基于文本的数据)的安全决策,则该操作应该使用序号比较而不是区分区域性的比较。这是因为根据起作用的区域性的不同,区分区域性的比较可产生不同的结果,而序号比较则仅依赖于所比较字符的二进制值。
字符串留用
公共语言运行库通过维护一个表来存放字符串,该表称为拘留池,它包含程序中以编程方式声明或创建的每个唯一的字符串的一个引用。因此,具有特定值的字符串的实例在系统中只有一个。
例如,如果将同一字符串分配给几个变量,运行库就会从拘留池中检索对该字符串的相同引用,并将它分配给各个变量。
Intern 方法使用拘留池来搜索与 str 值相等的字符串。如果存在这样的字符串,则返回拘留池中它的引用。如果不存在,则向拘留池添加对 str 的引用,然后返回该引用。
在下面的 C# 示例中,值为“MyTest”的字符串 s1 已经留用,因为它在程序中是一个字符串常量。
System.Text.StringBuilder 类生成与 s1 同值的新字符串对象。对该字符串的引用被分配给 s2。
Intern 方法搜索与 s2 具有相同值的字符串。由于存在这样的字符串,该方法会返回分配给 s1 的同一引用,然后将该引用分配给 s3。
引用 s1 和 s2 的比较结果是不相等的,这是因为它们引用的是不同的对象;而引用 s1 和 s3 的比较结果是相等的,因为它们引用的是相同的字符串。
|
复制代码 |
String s1 = "MyTest"; String s2 = new StringBuilder().Append("My").Append("Test").ToString(); String s3 = String.Intern(s2); Console.WriteLine((Object)s2==(Object)s1); // Different references. Console.WriteLine((Object)s3==(Object)s1); // The same reference |