添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
心软的火锅  ·  urlEscapeA 函数 ...·  1 年前    · 
坏坏的蚂蚁  ·  spring quartz ...·  1 年前    · 
礼貌的风衣  ·  webpack-dev-server ...·  1 年前    · 
public ref class System::String sealed : IComparable, IComparable<System::String ^>, IConvertible, IEquatable<System::String ^>, System::Collections::Generic::IEnumerable<char>
public ref class System::String sealed : ICloneable, IComparable, IComparable<System::String ^>, IConvertible, IEquatable<System::String ^>, System::Collections::Generic::IEnumerable<char>
public ref class System::String sealed : ICloneable, IComparable, IComparable<System::String ^>, IConvertible, IEquatable<System::String ^>, IParsable<System::String ^>, ISpanParsable<System::String ^>, System::Collections::Generic::IEnumerable<char>
public ref class System::String sealed : ICloneable, IComparable, IConvertible, System::Collections::IEnumerable
public ref class System::String sealed : IComparable, IComparable<System::String ^>, IEquatable<System::String ^>, System::Collections::IEnumerable
public ref class System::String sealed : IComparable, IComparable<System::String ^>, IEquatable<System::String ^>, System::Collections::Generic::IEnumerable<char>
public sealed class String : IComparable, IComparable<string>, IConvertible, IEquatable<string>, System.Collections.Generic.IEnumerable<char>
public sealed class String : ICloneable, IComparable, IComparable<string>, IConvertible, IEquatable<string>, System.Collections.Generic.IEnumerable<char>
public sealed class String : ICloneable, IComparable, IComparable<string>, IConvertible, IEquatable<string>, IParsable<string>, ISpanParsable<string>, System.Collections.Generic.IEnumerable<char>
[System.Serializable]
public sealed class String : ICloneable, IComparable, IConvertible, System.Collections.IEnumerable
[System.Serializable]
[System.Runtime.InteropServices.ComVisible(true)]
public sealed class String : ICloneable, IComparable, IComparable<string>, IConvertible, IEquatable<string>, System.Collections.Generic.IEnumerable<char>
public sealed class String : IComparable, IComparable<string>, IEquatable<string>, System.Collections.IEnumerable
public sealed class String : IComparable, IComparable<string>, IEquatable<string>, System.Collections.Generic.IEnumerable<char>
type string = class
    interface seq<char>
    interface IEnumerable
    interface IComparable
    interface IComparable<string>
    interface IConvertible
    interface IEquatable<string>
type string = class
    interface seq<char>
    interface IEnumerable
    interface ICloneable
    interface IComparable
    interface IComparable<string>
    interface IConvertible
    interface IEquatable<string>
type string = class
    interface seq<char>
    interface IEnumerable
    interface ICloneable
    interface IComparable
    interface IComparable<string>
    interface IConvertible
    interface IEquatable<string>
    interface IParsable<string>
    interface ISpanParsable<string>
type string = class
    interface seq<char>
    interface IEnumerable
    interface IComparable
    interface IComparable<string>
    interface IConvertible
    interface IEquatable<string>
    interface ICloneable
[<System.Serializable>]
type string = class
    interface IComparable
    interface ICloneable
    interface IConvertible
    interface IEnumerable
[<System.Serializable>]
[<System.Runtime.InteropServices.ComVisible(true)>]
type string = class
    interface IComparable
    interface ICloneable
    interface IConvertible
    interface IComparable<string>
    interface seq<char>
    interface IEnumerable
    interface IEquatable<string>
[<System.Serializable>]
[<System.Runtime.InteropServices.ComVisible(true)>]
type string = class
    interface IComparable
    interface ICloneable
    interface IConvertible
    interface IEnumerable
    interface IComparable<string>
    interface seq<char>
    interface IEquatable<string>
type string = class
    interface IEnumerable
    interface IComparable
    interface IComparable<string>
    interface IEquatable<string>
type string = class
    interface IComparable
    interface IComparable<string>
    interface IEquatable<string>
    interface seq<char>
    interface IEnumerable
Public NotInheritable Class String
Implements IComparable, IComparable(Of String), IConvertible, IEnumerable(Of Char), IEquatable(Of String)
Public NotInheritable Class String
Implements ICloneable, IComparable, IComparable(Of String), IConvertible, IEnumerable(Of Char), IEquatable(Of String)
Public NotInheritable Class String
Implements ICloneable, IComparable, IComparable(Of String), IConvertible, IEnumerable(Of Char), IEquatable(Of String), IParsable(Of String), ISpanParsable(Of String)
Public NotInheritable Class String
Implements ICloneable, IComparable, IConvertible, IEnumerable
Public NotInheritable Class String
Implements IComparable, IComparable(Of String), IEnumerable, IEquatable(Of String)
Public NotInheritable Class String
Implements IComparable, IComparable(Of String), IEnumerable(Of Char), IEquatable(Of String)
Object
String

字符串是用于表示文本的字符的顺序集合。 String 对象是表示字符串的 System.Char 对象的连续集合; System.Char 对象对应于 UTF-16 代码单元。 对象的值 String 是对象的顺序集合 System.Char 的内容,该值是不可变的, (即,它是只读的) 。 有关字符串不可变性的详细信息,请参阅 不可变性和 StringBuilder 类 部分。 内存中对象的最大大小 String 为 2-GB,或大约 10 亿个字符。

有关 Unicode、UTF-16、代码单元、码位以及 Char Rune 类型的详细信息,请参阅 .NET 中的字符编码简介

本节内容:

实例化 String 对象
Char 对象和 Unicode 字符
字符串和 Unicode 标准
字符串和嵌入的 null 字符
字符串和索引
Null 字符串和空字符串
不可变性和 StringBuilder 类
序号与区域性敏感型操作
按类别划分的字符串操作

实例化 String 对象

可以通过以下方式实例化 String 对象:

  • 通过将字符串文本分配给 String 变量。 这是用于创建字符串的最常用方法。 以下示例使用赋值创建多个字符串。 请注意,在 C# 和 F# 中,由于反斜杠 (\) 是转义字符,因此字符串中的文本反斜杠必须进行转义或整个字符串必须是 @-quoted。

    using namespace System; void main() String^ string1 = "This is a string created by assignment."; Console::WriteLine(string1); String^ string2a = "The path is C:\\PublicDocuments\\Report1.doc"; Console::WriteLine(string2a); // The example displays the following output: // This is a string created by assignment. // The path is C:\PublicDocuments\Report1.doc string string1 = "This is a string created by assignment."; Console.WriteLine(string1); string string2a = "The path is C:\\PublicDocuments\\Report1.doc"; Console.WriteLine(string2a); string string2b = @"The path is C:\PublicDocuments\Report1.doc"; Console.WriteLine(string2b); // The example displays the following output: // This is a string created by assignment. // The path is C:\PublicDocuments\Report1.doc // The path is C:\PublicDocuments\Report1.doc let string1 = "This is a string created by assignment." printfn "%s" string1 let string2a = "The path is C:\\PublicDocuments\\Report1.doc" printfn "%s" string2a let string2b = @"The path is C:\PublicDocuments\Report1.doc" printfn "%s" string2b // The example displays the following output: // This is a string created by assignment. // The path is C:\PublicDocuments\Report1.doc // The path is C:\PublicDocuments\Report1.doc Dim string1 As String = "This is a string created by assignment." Console.WriteLine(string1) Dim string2 As String = "The path is C:\PublicDocuments\Report1.doc" Console.WriteLine(string2) ' The example displays the following output: ' This is a string created by assignment. ' The path is C:\PublicDocuments\Report1.doc
  • 通过调用 String 类构造函数。 以下示例通过调用多个类构造函数来实例化字符串。 请注意,某些构造函数包括指向字符数组或带符号字节数组的指针作为参数。 Visual Basic 不支持调用这些构造函数。 有关构造函数的详细信息 String ,请参阅 String 构造函数摘要。

    using namespace System; void main() wchar_t chars[5] = L"word"; char bytes[6] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x00 }; // Create a string from a character array. String^ string1 = gcnew String(chars); Console::WriteLine(string1); // Create a string that consists of a character repeated 20 times. String^ string2 = gcnew String('c', 20); Console::WriteLine(string2); String^ stringFromBytes = nullptr; String^ stringFromChars = nullptr; char * pbytes = &bytes[0]; // Create a string from a pointer to a signed byte array. stringFromBytes = gcnew String(pbytes); wchar_t* pchars = &chars[0]; // Create a string from a pointer to a character array. stringFromChars = gcnew String(pchars); Console::WriteLine(stringFromBytes); Console::WriteLine(stringFromChars); Console::ReadLine(); // The example displays the following output: // word // cccccccccccccccccccc // ABCDE // word char[] chars = { 'w', 'o', 'r', 'd' }; sbyte[] bytes = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x00 }; // Create a string from a character array. string string1 = new string(chars); Console.WriteLine(string1); // Create a string that consists of a character repeated 20 times. string string2 = new string('c', 20); Console.WriteLine(string2); string stringFromBytes = null; string stringFromChars = null; unsafe fixed (sbyte* pbytes = bytes) // Create a string from a pointer to a signed byte array. stringFromBytes = new string(pbytes); fixed (char* pchars = chars) // Create a string from a pointer to a character array. stringFromChars = new string(pchars); Console.WriteLine(stringFromBytes); Console.WriteLine(stringFromChars); // The example displays the following output: // word // cccccccccccccccccccc // ABCDE // word let chars = [| 'w'; 'o'; 'r'; 'd' |] let bytes = [| 0x41y; 0x42y; 0x43y; 0x44y; 0x45y; 0x00y |] // Create a string from a character array. let string1 = String chars printfn "%s" string1 // Create a string that consists of a character repeated 20 times. let string2 = String('c', 20) printfn "%s" string2 let stringFromBytes = // Create a string from a pointer to a signed byte array. use pbytes = fixed bytes String pbytes let stringFromChars = // Create a string from a pointer to a character array. use pchars = fixed chars String pchars printfn $"{stringFromBytes}" printfn $"{stringFromChars}" // The example displays the following output: // word // cccccccccccccccccccc // ABCDE // word Dim chars() As Char = { "w"c, "o"c, "r"c, "d"c } ' Create a string from a character array. Dim string1 As New String(chars) Console.WriteLine(string1) ' Create a string that consists of a character repeated 20 times. Dim string2 As New String("c"c, 20) Console.WriteLine(string2) ' The example displays the following output: ' word ' cccccccccccccccccccc
  • 通过在 C# 和 F# 中使用字符串串联运算符 (+, & 在 Visual Basic) 中使用 或 + 从实例和字符串文本的 String 任意组合创建单个字符串。 以下示例演示了字符串串联运算符的用法。

    String^ string1 = "Today is " + DateTime::Now.ToString("D") + "."; Console::WriteLine(string1); String^ string2 = "This is one sentence. " + "This is a second. "; string2 += "This is a third sentence."; Console::WriteLine(string2); // The example displays output like the following: // Today is Tuesday, July 06, 2011. // This is one sentence. This is a second. This is a third sentence. string string1 = "Today is " + DateTime.Now.ToString("D") + "."; Console.WriteLine(string1); string string2 = "This is one sentence. " + "This is a second. "; string2 += "This is a third sentence."; Console.WriteLine(string2); // The example displays output like the following: // Today is Tuesday, July 06, 2011. // This is one sentence. This is a second. This is a third sentence. let string1 = "Today is " + DateTime.Now.ToString("D") + "." printfn $"{string1}" let string2 = "This is one sentence. " + "This is a second. " let string2 = string2 + "This is a third sentence." printfn $"{string2}" // The example displays output like the following: // Today is Tuesday, July 06, 2011. // This is one sentence. This is a second. This is a third sentence. Dim string1 As String = "Today is " + Date.Now.ToString("D") + "." Console.WriteLine(string1) Dim string2 As String = "This is one sentence. " + "This is a second. " string2 += "This is a third sentence." Console.WriteLine(string2) ' The example displays output like the following: ' Today is Tuesday, July 06, 2011. ' This is one sentence. This is a second. This is a third sentence.
  • 通过检索属性或调用返回字符串的方法。 以下示例使用 类的 String 方法从较大的字符串中提取子字符串。

    String^ sentence = "This sentence has five words."; // Extract the second word. int startPosition = sentence->IndexOf(" ") + 1; String^ word2 = sentence->Substring(startPosition, sentence->IndexOf(" ", startPosition) - startPosition); Console::WriteLine("Second word: " + word2); string sentence = "This sentence has five words."; // Extract the second word. int startPosition = sentence.IndexOf(" ") + 1; string word2 = sentence.Substring(startPosition, sentence.IndexOf(" ", startPosition) - startPosition); Console.WriteLine("Second word: " + word2); // The example displays the following output: // Second word: sentence let sentence = "This sentence has five words." // Extract the second word. let startPosition = sentence.IndexOf " " + 1 let word2 = sentence.Substring(startPosition, sentence.IndexOf(" ", startPosition) - startPosition) printfn $"Second word: {word2}" // The example displays the following output: // Second word: sentence Dim sentence As String = "This sentence has five words." ' Extract the second word. Dim startPosition As Integer = sentence.IndexOf(" ") + 1 Dim word2 As String = sentence.Substring(startPosition, sentence.IndexOf(" ", startPosition) - startPosition) Console.WriteLine("Second word: " + word2) ' The example displays the following output: ' Second word: sentence
  • 通过调用格式设置方法将值或对象转换为其字符串表示形式。 以下示例使用 复合格式设置 功能将两个 对象的字符串表示形式嵌入到字符串中。

    DateTime^ dateAndTime = gcnew DateTime(2011, 7, 6, 7, 32, 0); Double temperature = 68.3; String^ result = String::Format("At {0:t} on {0:D}, the temperature was {1:F1} degrees Fahrenheit.", dateAndTime, temperature); Console::WriteLine(result); // The example displays the following output: // At 7:32 AM on Wednesday, July 06, 2011, the temperature was 68.3 degrees Fahrenheit. DateTime dateAndTime = new DateTime(2011, 7, 6, 7, 32, 0); double temperature = 68.3; string result = String.Format("At {0:t} on {0:D}, the temperature was {1:F1} degrees Fahrenheit.", dateAndTime, temperature); Console.WriteLine(result); // The example displays the following output: // At 7:32 AM on Wednesday, July 06, 2011, the temperature was 68.3 degrees Fahrenheit. let dateAndTime = DateTime(2011, 7, 6, 7, 32, 0) let temperature = 68.3 String.Format("At {0:t} on {0:D}, the temperature was {1:F1} degrees Fahrenheit.", dateAndTime, temperature) |> printfn "%s" // The example displays the following output: // At 7:32 AM on Wednesday, July 06, 2011, the temperature was 68.3 degrees Fahrenheit. Dim dateAndTime As DateTime = #07/06/2011 7:32:00AM# Dim temperature As Double = 68.3 Dim result As String = String.Format("At {0:t} on {0:D}, the temperature was {1:F1} degrees Fahrenheit.", dateAndTime, temperature) Console.WriteLine(result) ' The example displays the following output: ' At 7:32 AM on Wednesday, July 06, 2011, the temperature was 68.3 degrees Fahrenheit.

    Char 对象和 Unicode 字符

    字符串中的每个字符都由 Unicode 标量值定义,也称为 Unicode 码位或 Unicode 字符的序号 (数值) 值。 每个码位都使用 UTF-16 编码进行编码,编码的每个元素的数值由 对象 Char 表示。

    请注意,由于 String 实例由 UTF-16 代码单元的顺序集合组成,因此可以创建一个 String 不是格式正确的 Unicode 字符串的对象。 例如,可以创建一个字符串,该字符串具有低代理项,而没有相应的高代理项。 尽管某些方法(例如命名空间中 System.Text 对对象进行编码和解码的方法)可能会执行检查以确保字符串格式正常, String 但类成员不确保字符串格式正常。

    单个 Char 对象通常表示单个码位;也就是说, 的 Char 数值等于码位。 例如,字符“a”的代码点为 U+0061。 但是,代码点可能需要多个编码元素 ( Char 多个对象) 。 Unicode 标准定义了两种类型的字符,这些字符对应于多个 Char 对象:graphemes 和 Unicode 补充代码点,这些代码点对应于 Unicode 补充平面中的字符。

  • grapheme 由基字符表示,后跟一个或多个组合字符。 例如,字符 ä 由 Char 代码点为 U+0061 的对象表示,后跟 Char 代码点为 U+0308 的对象。 此字符也可以由代码点为 U+00E4 的单个 Char 对象定义。 如以下示例所示,区分区域性的相等性比较指示这两种表示形式相等,但普通序号比较则不相等。 但是,如果两个字符串已规范化,则序号比较也会指示它们相等。 (有关规范化字符串的详细信息,请参阅 规范化 部分。)

    using namespace System; using namespace System::Globalization; using namespace System::IO; void main() StreamWriter^ sw = gcnew StreamWriter(".\\graphemes.txt"); String^ grapheme = L"a" + L"\u0308"; sw->WriteLine(grapheme); String^ singleChar = "\u00e4"; sw->WriteLine(singleChar); sw->WriteLine("{0} = {1} (Culture-sensitive): {2}", grapheme, singleChar, String::Equals(grapheme, singleChar, StringComparison::CurrentCulture)); sw->WriteLine("{0} = {1} (Ordinal): {2}", grapheme, singleChar, String::Equals(grapheme, singleChar, StringComparison::Ordinal)); sw->WriteLine("{0} = {1} (Normalized Ordinal): {2}", grapheme, singleChar, String::Equals(grapheme->Normalize(), singleChar->Normalize(), StringComparison::Ordinal)); sw->Close(); // The example produces the following output: // ä // ä // ä = ä (Culture-sensitive): True // ä = ä (Ordinal): False // ä = ä (Normalized Ordinal): True using System; using System.Globalization; using System.IO; public class Example public static void Main() StreamWriter sw = new StreamWriter(@".\graphemes.txt"); string grapheme = "\u0061\u0308"; sw.WriteLine(grapheme); string singleChar = "\u00e4"; sw.WriteLine(singleChar); sw.WriteLine("{0} = {1} (Culture-sensitive): {2}", grapheme, singleChar, String.Equals(grapheme, singleChar, StringComparison.CurrentCulture)); sw.WriteLine("{0} = {1} (Ordinal): {2}", grapheme, singleChar, String.Equals(grapheme, singleChar, StringComparison.Ordinal)); sw.WriteLine("{0} = {1} (Normalized Ordinal): {2}", grapheme, singleChar, String.Equals(grapheme.Normalize(), singleChar.Normalize(), StringComparison.Ordinal)); sw.Close(); // The example produces the following output: // ä // ä // ä = ä (Culture-sensitive): True // ä = ä (Ordinal): False // ä = ä (Normalized Ordinal): True open System open System.IO use sw = new StreamWriter(@".\graphemes.txt") let grapheme = "\u0061\u0308" sw.WriteLine grapheme let singleChar = "\u00e4" sw.WriteLine singleChar sw.WriteLine("{0} = {1} (Culture-sensitive): {2}", grapheme, singleChar, String.Equals(grapheme, singleChar, StringComparison.CurrentCulture)) sw.WriteLine("{0} = {1} (Ordinal): {2}", grapheme, singleChar, String.Equals(grapheme, singleChar, StringComparison.Ordinal)) sw.WriteLine("{0} = {1} (Normalized Ordinal): {2}", grapheme, singleChar, String.Equals(grapheme.Normalize(), singleChar.Normalize(), StringComparison.Ordinal)) // The example produces the following output: // ä // ä // ä = ä (Culture-sensitive): True // ä = ä (Ordinal): False // ä = ä (Normalized Ordinal): True Imports System.Globalization Imports System.IO Module Example Public Sub Main() Dim sw As New StreamWriter(".\graphemes.txt") Dim grapheme As String = ChrW(&H0061) + ChrW(&h0308) sw.WriteLine(grapheme) Dim singleChar As String = ChrW(&h00e4) sw.WriteLine(singleChar) sw.WriteLine("{0} = {1} (Culture-sensitive): {2}", grapheme, singleChar, String.Equals(grapheme, singleChar, StringComparison.CurrentCulture)) sw.WriteLine("{0} = {1} (Ordinal): {2}", grapheme, singleChar, String.Equals(grapheme, singleChar, StringComparison.Ordinal)) sw.WriteLine("{0} = {1} (Normalized Ordinal): {2}", grapheme, singleChar, String.Equals(grapheme.Normalize(), singleChar.Normalize(), StringComparison.Ordinal)) sw.Close() End Sub End Module ' The example produces the following output: ' ä ' ä ' ä = ä (Culture-sensitive): True ' ä = ä (Ordinal): False ' ä = ä (Normalized Ordinal): True
  • ) 代理项对 (Unicode 补充码位由 Char 代码点为高代理项的对象表示,后跟 Char 代码点为低代理项的对象。 高代理项的代码单位范围从 U+D800 到 U+DBFF。 低代理项的代码单位范围从 U+DC00 到 U+DFFF。 代理项对用于表示 16 个 Unicode 补充平面中的字符。 以下示例创建一个代理项字符,并将其 Char.IsSurrogatePair(Char, Char) 传递给 方法,以确定它是否为代理项对。

    using namespace System; void main() String^ surrogate = L"\xD800\xDC03" ; for (int ctr = 0; ctr < surrogate->Length; ctr++) Console::Write("U+{0:X4} ", Convert::ToUInt16(surrogate[ctr])); Console::WriteLine(); Console::WriteLine(" Is Surrogate Pair: {0}", Char::IsSurrogatePair(surrogate[0], surrogate[1])); Console::ReadLine(); // The example displays the following output: // U+D800 U+DC03 // Is Surrogate Pair: True string surrogate = "\uD800\uDC03"; for (int ctr = 0; ctr < surrogate.Length; ctr++) Console.Write($"U+{(ushort)surrogate[ctr]:X2} "); Console.WriteLine(); Console.WriteLine(" Is Surrogate Pair: {0}", Char.IsSurrogatePair(surrogate[0], surrogate[1])); // The example displays the following output: // U+D800 U+DC03 // Is Surrogate Pair: True open System let surrogate = "\uD800\uDC03" for i = 0 to surrogate.Length - 1 do printf $"U+{uint16 surrogate[i]:X2} " printfn $"\n Is Surrogate Pair: {Char.IsSurrogatePair(surrogate[0], surrogate[1])}" // The example displays the following output: // U+D800 U+DC03 // Is Surrogate Pair: True Module Example Public Sub Main() Dim surrogate As String = ChrW(&hD800) + ChrW(&hDC03) For ctr As Integer = 0 To surrogate.Length - 1 Console.Write("U+{0:X2} ", Convert.ToUInt16(surrogate(ctr))) Console.WriteLine() Console.WriteLine(" Is Surrogate Pair: {0}", Char.IsSurrogatePair(surrogate(0), surrogate(1))) End Sub End Module ' The example displays the following output: ' U+D800 U+DC03 ' Is Surrogate Pair: True

    字符串和 Unicode 标准

    字符串中的字符由对应于值的 UTF-16 编码代码单元 Char 表示。

    字符串中的每个字符都有一个关联的 Unicode 字符类别,该类别在 .NET 中由 UnicodeCategory 枚举表示。 可以通过调用 方法来确定字符或代理项对的 CharUnicodeInfo.GetUnicodeCategory 类别。

    .NET 维护自己的字符表及其相应的类别,这可确保在不同平台上运行的特定版本的 .NET 实现返回相同的字符类别信息。 在所有 .NET 版本和所有 OS 平台上,字符类别信息由 Unicode 字符数据库 提供。

    下表列出了 .NET 版本及其字符类别所基于的 Unicode 标准。

    .NET 版本 Unicode 标准版本

    此外,.NET 支持基于 Unicode 标准的字符串比较和排序。 .NET Framework 4 和早期版本维护自己的字符串数据表。 从 Windows 7 上运行的 .NET Framework 4.5 开始,.NET Framework版本也是如此。 从在 Windows 8 及更高版本的 Windows 操作系统上运行的 .NET Framework 4.5 开始,运行时会将字符串比较和排序操作委托给操作系统。 在 .NET Core 和 .NET 5+ 上,字符串比较和排序信息由 Unicode 库的国际组件 (提供,但 Windows 版本低于 Windows 10 2019 年 5 月更新) 。 下表列出了字符比较和排序所基于的 .NET 版本和 Unicode Standard 版本。

    .NET 版本 Unicode 标准版本

    字符串和嵌入的 null 字符

    在 .NET 中 String ,对象可以包含嵌入的空字符,这些字符算作字符串长度的一部分。 但是,在某些语言(如 C 和 C++)中,null 字符表示字符串的末尾;它不被视为字符串的一部分,也不计入字符串长度的一部分。 这意味着,以下常见假设 C 和 C++ 程序员或用 C++ 编写的库在应用于 String 对象时不一定有效:

  • 或 函数返回 strlen 的值不一定等于 String.Length wcslen

  • strcpy_s wcscpy_s 函数创建的字符串不一定与 方法 String.Copy 创建的字符串相同。

    应确保实例化 String 对象的本机 C 和 C++ 代码以及通过平台调用传递 String 对象的代码不假定嵌入的 null 字符标记字符串的末尾。

    当字符串 (排序或比较) 以及搜索字符串时,字符串中嵌入的 null 字符的处理方式也会有所不同。 在两个字符串之间执行区分区域性的比较(包括使用固定区域性的比较)时,将忽略 Null 字符。 它们仅考虑用于序号或不区分大小写的序号比较。 另一方面,使用 、 和 IndexOf Contains StartsWith 方法搜索字符串时,始终考虑嵌入的空字符。

    字符串和索引

    索引是 对象 (而不是 Unicode 字符在 中 String ) 的位置 Char 。 索引是从零开始的非数值,从字符串中的第一个位置开始,即索引位置零。 许多搜索方法(如 IndexOf LastIndexOf )在字符串实例中返回字符或子字符串的索引。

    属性 Chars[] 允许按单个 Char 对象在字符串中的索引位置来访问这些对象。 Chars[] 由于 属性是 Visual Basic () 中的默认属性或 C# 和 F#) 中的索引器 (,因此可以使用如下所示的代码访问字符串中的单个 Char 对象。 此代码在字符串中查找空格或标点字符,以确定字符串包含的单词数。

    using namespace System; void main() String^ s1 = "This string consists of a single short sentence."; int nWords = 0; s1 = s1->Trim(); for (int ctr = 0; ctr < s1->Length; ctr++) { if (Char::IsPunctuation(s1[ctr]) | Char::IsWhiteSpace(s1[ctr])) nWords++; Console::WriteLine("The sentence\n {0}\nhas {1} words.", s1, nWords); // The example displays the following output: // The sentence // This string consists of a single short sentence. // has 8 words. string s1 = "This string consists of a single short sentence."; int nWords = 0; s1 = s1.Trim(); for (int ctr = 0; ctr < s1.Length; ctr++) { if (Char.IsPunctuation(s1[ctr]) | Char.IsWhiteSpace(s1[ctr])) nWords++; Console.WriteLine("The sentence\n {0}\nhas {1} words.", s1, nWords); // The example displays the following output: // The sentence // This string consists of a single short sentence. // has 8 words. let s1 = "This string consists of a single short sentence." let mutable nWords = 0 for i = 0 to s1.Length - 1 do if Char.IsPunctuation s1[i] || Char.IsWhiteSpace s1[i] then nWords <- nWords + 1 printfn $"The sentence\n {s1}\nhas {nWords} words." // The example displays the following output: // The sentence // This string consists of a single short sentence. // has 8 words. Module Example Public Sub Main() Dim s1 As String = "This string consists of a single short sentence." Dim nWords As Integer = 0 s1 = s1.Trim() For ctr As Integer = 0 To s1.Length - 1 If Char.IsPunctuation(s1(ctr)) Or Char.IsWhiteSpace(s1(ctr)) nWords += 1 End If Console.WriteLine("The sentence{2} {0}{2}has {1} words.", s1, nWords, vbCrLf) End Sub End Module ' The example displays the following output: ' The sentence ' This string consists of a single short sentence. ' has 8 words.

    String 由于 类实现 IEnumerable 接口,因此还可以使用 foreach 构造循环访问 Char 字符串中的 对象,如以下示例所示。

    using namespace System; void main() String^ s1 = "This string consists of a single short sentence."; int nWords = 0; s1 = s1->Trim(); for each (Char ch in s1) if (Char::IsPunctuation(ch) | Char::IsWhiteSpace(ch)) nWords++; Console::WriteLine("The sentence\n {0}\nhas {1} words.", s1, nWords); Console::ReadLine(); // The example displays the following output: // The sentence // This string consists of a single short sentence. // has 8 words. string s1 = "This string consists of a single short sentence."; int nWords = 0; s1 = s1.Trim(); foreach (var ch in s1) { if (Char.IsPunctuation(ch) | Char.IsWhiteSpace(ch)) nWords++; Console.WriteLine("The sentence\n {0}\nhas {1} words.", s1, nWords); // The example displays the following output: // The sentence // This string consists of a single short sentence. // has 8 words. let s1 = "This string consists of a single short sentence." let mutable nWords = 0 for ch in s1 do if Char.IsPunctuation ch || Char.IsWhiteSpace ch then nWords <- nWords + 1 printfn $"The sentence\n {s1}\nhas {nWords} words." // The example displays the following output: // The sentence // This string consists of a single short sentence. // has 8 words. Module Example Public Sub Main() Dim s1 As String = "This string consists of a single short sentence." Dim nWords As Integer = 0 s1 = s1.Trim() For Each ch In s1 If Char.IsPunctuation(ch) Or Char.IsWhiteSpace(ch) Then nWords += 1 End If Console.WriteLine("The sentence{2} {0}{2}has {1} words.", s1, nWords, vbCrLf) End Sub End Module ' The example displays the following output: ' The sentence ' This string consists of a single short sentence. ' has 8 words.

    连续索引值可能不对应于连续的 Unicode 字符,因为 Unicode 字符可能编码为多个 Char 对象。 具体而言,字符串可能包含由基字符、后跟一个或多个组合字符或代理项对构成的多字符文本单元。 若要使用 Unicode 字符而不是 Char 对象,请使用 System.Globalization.StringInfo TextElementEnumerator 类或 String.EnumerateRunes 方法和 Rune 结构。 以下示例演示了使用 Char 对象的代码和使用 Unicode 字符的代码之间的区别。 它比较句子中每个单词中的字符数或文本元素数。 字符串包含两个基字符序列,后跟一个组合字符。

    using namespace System; using namespace System::Collections::Generic; using namespace System::Globalization; void main() // First sentence of The Mystery of the Yellow Room, by Leroux. String^ opening = L"Ce n'est pas sans une certaine émotion que "+ L"je commence à raconter ici les aventures " + L"extraordinaires de Joseph Rouletabille."; // Character counters. int nChars = 0; // Objects to store word count. List<int>^ chars = gcnew List<int>(); List<int>^ elements = gcnew List<int>(); for each (Char ch in opening) { // Skip the ' character. if (ch == '\x0027') continue; if (Char::IsWhiteSpace(ch) | (Char::IsPunctuation(ch))) { chars->Add(nChars); nChars = 0; else { nChars++; TextElementEnumerator^ te = StringInfo::GetTextElementEnumerator(opening); while (te->MoveNext()) { String^ s = te->GetTextElement(); // Skip the ' character. if (s == "\x0027") continue; if ( String::IsNullOrEmpty(s->Trim()) | (s->Length == 1 && Char::IsPunctuation(Convert::ToChar(s)))) { elements->Add(nChars); nChars = 0; else { nChars++; // Display character counts. Console::WriteLine("{0,6} {1,20} {2,20}", "Word #", "Char Objects", "Characters"); for (int ctr = 0; ctr < chars->Count; ctr++) Console::WriteLine("{0,6} {1,20} {2,20}", ctr, chars[ctr], elements[ctr]); Console::ReadLine(); // The example displays the following output: // Word # Char Objects Characters // 0 2 2 // 1 4 4 // 2 3 3 // 3 4 4 // 4 3 3 // 5 8 8 // 6 8 7 // 7 3 3 // 8 2 2 // 9 8 8 // 10 2 1 // 11 8 8 // 12 3 3 // 13 3 3 // 14 9 9 // 15 15 15 // 16 2 2 // 17 6 6 // 18 12 12 // First sentence of The Mystery of the Yellow Room, by Leroux. string opening = "Ce n'est pas sans une certaine émotion que "+ "je commence à raconter ici les aventures " + "extraordinaires de Joseph Rouletabille."; // Character counters. int nChars = 0; // Objects to store word count. List<int> chars = new List<int>(); List<int> elements = new List<int>(); foreach (var ch in opening) { // Skip the ' character. if (ch == '\u0027') continue; if (Char.IsWhiteSpace(ch) | (Char.IsPunctuation(ch))) { chars.Add(nChars); nChars = 0; else { nChars++; System.Globalization.TextElementEnumerator te = System.Globalization.StringInfo.GetTextElementEnumerator(opening); while (te.MoveNext()) { string s = te.GetTextElement(); // Skip the ' character. if (s == "\u0027") continue; if ( String.IsNullOrEmpty(s.Trim()) | (s.Length == 1 && Char.IsPunctuation(Convert.ToChar(s)))) { elements.Add(nChars); nChars = 0; else { nChars++; // Display character counts. Console.WriteLine("{0,6} {1,20} {2,20}", "Word #", "Char Objects", "Characters"); for (int ctr = 0; ctr < chars.Count; ctr++) Console.WriteLine("{0,6} {1,20} {2,20}", ctr, chars[ctr], elements[ctr]); // The example displays the following output: // Word # Char Objects Characters // 0 2 2 // 1 4 4 // 2 3 3 // 3 4 4 // 4 3 3 // 5 8 8 // 6 8 7 // 7 3 3 // 8 2 2 // 9 8 8 // 10 2 1 // 11 8 8 // 12 3 3 // 13 3 3 // 14 9 9 // 15 15 15 // 16 2 2 // 17 6 6 // 18 12 12 open System open System.Globalization // First sentence of The Mystery of the Yellow Room, by Leroux. let opening = "Ce n'est pas sans une certaine émotion que je commence à raconter ici les aventures extraordinaires de Joseph Rouletabille." // Character counters. let mutable nChars = 0 // Objects to store word count. let chars = ResizeArray<int>() let elements = ResizeArray<int>() for ch in opening do // Skip the ' character. if ch <> '\u0027' then if Char.IsWhiteSpace ch || Char.IsPunctuation ch then chars.Add nChars nChars <- 0 nChars <- nChars + 1 let te = StringInfo.GetTextElementEnumerator opening while te.MoveNext() do let s = te.GetTextElement() // Skip the ' character. if s <> "\u0027" then if String.IsNullOrEmpty(s.Trim()) || (s.Length = 1 && Char.IsPunctuation(Convert.ToChar s)) then elements.Add nChars nChars <- 0 nChars <- nChars + 1 // Display character counts. printfn "%6s %20s %20s" "Word #" "Char Objects " "Characters" for i = 0 to chars.Count - 1 do printfn "%6d %20d %20d" i chars[i] elements[i] // The example displays the following output: // Word # Char Objects Characters // 0 2 2 // 1 4 4 // 2 3 3 // 3 4 4 // 4 3 3 // 5 8 8 // 6 8 7 // 7 3 3 // 8 2 2 // 9 8 8 // 10 2 1 // 11 8 8 // 12 3 3 // 13 3 3 // 14 9 9 // 15 15 15 // 16 2 2 // 17 6 6 // 18 12 12 Imports System.Collections.Generic Imports System.Globalization Module Example Public Sub Main() ' First sentence of The Mystery of the Yellow Room, by Leroux. Dim opening As String = "Ce n'est pas sans une certaine émotion que "+ "je commence à raconter ici les aventures " + "extraordinaires de Joseph Rouletabille." ' Character counters. Dim nChars As Integer = 0 ' Objects to store word count. Dim chars As New List(Of Integer)() Dim elements As New List(Of Integer)() For Each ch In opening ' Skip the ' character. If ch = ChrW(&h0027) Then Continue For If Char.IsWhiteSpace(ch) Or Char.IsPunctuation(ch) Then chars.Add(nChars) nChars = 0 nChars += 1 End If Dim te As TextElementEnumerator = StringInfo.GetTextElementEnumerator(opening) Do While te.MoveNext() Dim s As String = te.GetTextElement() ' Skip the ' character. If s = ChrW(&h0027) Then Continue Do If String.IsNullOrEmpty(s.Trim()) Or (s.Length = 1 AndAlso Char.IsPunctuation(Convert.ToChar(s))) elements.Add(nChars) nChars = 0 nChars += 1 End If ' Display character counts. Console.WriteLine("{0,6} {1,20} {2,20}", "Word #", "Char Objects", "Characters") For ctr As Integer = 0 To chars.Count - 1 Console.WriteLine("{0,6} {1,20} {2,20}", ctr, chars(ctr), elements(ctr)) End Sub End Module ' The example displays the following output: ' Word # Char Objects Characters ' 0 2 2 ' 1 4 4 ' 2 3 3 ' 3 4 4 ' 4 3 3 ' 5 8 8 ' 6 8 7 ' 7 3 3 ' 8 2 2 ' 9 8 8 ' 10 2 1 ' 11 8 8 ' 12 3 3 ' 13 3 3 ' 14 9 9 ' 15 15 15 ' 16 2 2 ' 17 6 6 ' 18 12 12

    此示例使用 StringInfo.GetTextElementEnumerator 方法和 TextElementEnumerator 类枚举字符串中的所有文本元素,从而处理文本元素。 还可以通过调用 方法检索包含每个文本元素的起始索引的 StringInfo.ParseCombiningCharacters 数组。

    有关使用文本单位而不是单个 Char 值的详细信息,请参阅 .NET 中的字符编码简介

    Null 字符串和空字符串

    已声明但尚未分配值的字符串为 null 。 尝试在该字符串上调用方法会 NullReferenceException 引发 。 null 字符串不同于空字符串,空字符串是值为“”或 String.Empty 的字符串。 在某些情况下,在方法调用中将空字符串或空字符串作为参数传递会引发异常。 例如,将 null 字符串传递给 Int32.Parse 方法会引发 , ArgumentNullException 而传递空字符串会 FormatException 引发 。 在其他情况下,方法参数可以是空字符串或空字符串。 例如,如果要为类提供 IFormattable 实现,则希望将 null 字符串和空字符串与常规 (“G”) 格式说明符相等。

    String 包括以下两种方便方法,可用于测试字符串是 null 空还是为空:

  • IsNullOrEmpty ,指示字符串是 null 或 等于 String.Empty 。 此方法无需使用如下所示的代码:

    if (str == nullptr || str->Equals(String::Empty)) if (str == null || str.Equals(String.Empty)) if str = null || str.Equals String.Empty then If str Is Nothing OrElse str.Equals(String.Empty) Then
  • IsNullOrWhiteSpace ,指示字符串是 null 、等于 String.Empty 、 还是仅包含空格字符。 此方法无需使用如下所示的代码:

    if (str == nullptr || str->Equals(String::Empty) || str->Trim()->Equals(String::Empty)) if (str == null || str.Equals(String.Empty) || str.Trim().Equals(String.Empty)) if str = null || str.Equals String.Empty || str.Trim().Equals String.Empty then If str Is Nothing OrElse str.Equals(String.Empty) OrElse str.Trim().Equals(String.Empty)

    以下示例在 IsNullOrEmpty 自定义 Temperature 类的实现中使用 IFormattable.ToString 方法。 方法支持“G”、“C”、“F”和“K”格式字符串。 如果空格式字符串或值 null 传递给 方法的格式字符串,则其值将更改为“G”格式字符串。

    public: virtual String^ ToString(String^ format, IFormatProvider^ provider) if (String::IsNullOrEmpty(format)) format = "G"; if (provider == nullptr) provider = CultureInfo::CurrentCulture; switch (Convert::ToUInt16(format->ToUpperInvariant())) // Return degrees in Celsius. case 'G': case 'C': return temp.ToString("F2", provider) + L"�C"; // Return degrees in Fahrenheit. case 'F': return (temp * 9 / 5 + 32).ToString("F2", provider) + L"�F"; // Return degrees in Kelvin. case 'K': return (temp + 273.15).ToString(); default: throw gcnew FormatException( String::Format("The {0} format string is not supported.", format)); public string ToString(string format, IFormatProvider provider) if (String.IsNullOrEmpty(format)) format = "G"; if (provider == null) provider = CultureInfo.CurrentCulture; switch (format.ToUpperInvariant()) // Return degrees in Celsius. case "G": case "C": return temp.ToString("F2", provider) + "°C"; // Return degrees in Fahrenheit. case "F": return (temp * 9 / 5 + 32).ToString("F2", provider) + "°F"; // Return degrees in Kelvin. case "K": return (temp + 273.15).ToString(); default: throw new FormatException( String.Format("The {0} format string is not supported.", format)); member _.ToString(format: string, provider: IFormatProvider) = let format = if String.IsNullOrEmpty format then "G" else format let provider: IFormatProvider = if provider = null then CultureInfo.CurrentCulture else provider match format.ToUpperInvariant() with // Return degrees in Celsius. | "G" | "C" -> temp.ToString("F2", provider) + "°C" // Return degrees in Fahrenheit. | "F" -> (temp * 9. / 5. + 32.).ToString("F2", provider) + "°F" // Return degrees in Kelvin. | "K" -> (temp + 273.15).ToString() | _ -> raise (FormatException(String.Format("The {0} format string is not supported.",format))) Public Overloads Function ToString(fmt As String, provider As IFormatProvider) As String _ Implements IFormattable.ToString If String.IsNullOrEmpty(fmt) Then fmt = "G" If provider Is Nothing Then provider = CultureInfo.CurrentCulture Select Case fmt.ToUpperInvariant() ' Return degrees in Celsius. Case "G", "C" Return temp.ToString("F2", provider) + "°C" ' Return degrees in Fahrenheit. Case "F" Return (temp * 9 / 5 + 32).ToString("F2", provider) + "°F" ' Return degrees in Kelvin. Case "K" Return (temp + 273.15).ToString() Case Else Throw New FormatException( String.Format("The {0} format string is not supported.", fmt)) End Select End Function

    不可变性和 StringBuilder 类

    对象 String 称为不可变 (只读) ,因为它的值在创建后无法修改。 看起来用于修改 String 对象的方法实际上返回包含修改的新 String 对象。

    由于字符串是不可变的,因此对看似是单个字符串的内容执行重复添加或删除操作的字符串操作例程可能会造成严重的性能损失。 例如,以下代码使用随机数生成器创建一个字符串,该字符串在要0x052F 0x0001的范围内包含 1000 个字符。 尽管代码似乎使用字符串串联将新字符追加到名为 str 的现有字符串中,但它实际上会为每个串联操作创建一个新 String 对象。

    using namespace System; using namespace System::IO; using namespace System::Text; void main() Random^ rnd = gcnew Random(); String^ str = String::Empty; StreamWriter^ sw = gcnew StreamWriter(".\\StringFile.txt", false, Encoding::Unicode); for (int ctr = 0; ctr <= 1000; ctr++) { str += Convert::ToChar(rnd->Next(1, 0x0530)); if (str->Length % 60 == 0) str += Environment::NewLine; sw->Write(str); sw->Close(); using System; using System.IO; using System.Text; public class Example public static void Main() Random rnd = new Random(); string str = String.Empty; StreamWriter sw = new StreamWriter(@".\StringFile.txt", false, Encoding.Unicode); for (int ctr = 0; ctr <= 1000; ctr++) { str += (char)rnd.Next(1, 0x0530); if (str.Length % 60 == 0) str += Environment.NewLine; sw.Write(str); sw.Close(); open System open System.IO open System.Text let rnd = Random() let mutable str = String.Empty use sw = new StreamWriter(@".\StringFile.txt", false, Encoding.Unicode) for _ = 0 to 1000 do str <- str + (rnd.Next(1, 0x0530) |> char |> string) if str.Length % 60 = 0 then str <- str + Environment.NewLine sw.Write str Imports System.IO Imports System.Text Module Example Public Sub Main() Dim rnd As New Random() Dim str As String = String.Empty Dim sw As New StreamWriter(".\StringFile.txt", False, Encoding.Unicode) For ctr As Integer = 0 To 1000 str += ChrW(rnd.Next(1, &h0530)) If str.Length Mod 60 = 0 Then str += vbCrLf sw.Write(str) sw.Close() End Sub End Module

    对于对字符串的值进行多次更改的操作,可以使用 StringBuilder 类而不是 String 类。 与 类的 String 实例不同, StringBuilder 对象是可变的;从字符串中连接、追加或删除子字符串时,对单个字符串执行操作。 修改完对象的值 StringBuilder 后,可以调用其 StringBuilder.ToString 方法将其转换为字符串。 以下示例替换 String 上一个示例中用于连接范围中的 1000 个随机字符的 ,以0x0001 0x052F对象 StringBuilder

    using namespace System; using namespace System::IO; using namespace System::Text; void main() Random^ rnd = gcnew Random(); StringBuilder^ sb = gcnew StringBuilder(); StreamWriter^ sw = gcnew StreamWriter(".\\StringFile.txt", false, Encoding::Unicode); for (int ctr = 0; ctr <= 1000; ctr++) { sb->Append(Convert::ToChar(rnd->Next(1, 0x0530))); if (sb->Length % 60 == 0) sb->AppendLine(); sw->Write(sb->ToString()); sw->Close(); using System; using System.IO; using System.Text; public class Example public static void Main() Random rnd = new Random(); StringBuilder sb = new StringBuilder(); StreamWriter sw = new StreamWriter(@".\StringFile.txt", false, Encoding.Unicode); for (int ctr = 0; ctr <= 1000; ctr++) { sb.Append((char)rnd.Next(1, 0x0530)); if (sb.Length % 60 == 0) sb.AppendLine(); sw.Write(sb.ToString()); sw.Close(); open System open System.IO open System.Text let rnd = Random() let sb = StringBuilder() use sw = new StreamWriter(@".\StringFile.txt", false, Encoding.Unicode) for _ = 0 to 1000 do sb.Append(rnd.Next(1, 0x0530) |> char) |> ignore if sb.Length % 60 = 0 then sb.AppendLine() |> ignore sw.Write(string sb) Imports System.IO Imports System.Text Module Example Public Sub Main() Dim rnd As New Random() Dim sb As New StringBuilder() Dim sw As New StreamWriter(".\StringFile.txt", False, Encoding.Unicode) For ctr As Integer = 0 To 1000 sb.Append(ChrW(rnd.Next(1, &h0530))) If sb.Length Mod 60 = 0 Then sb.AppendLine() sw.Write(sb.ToString()) sw.Close() End Sub End Module

    序号与区域性敏感型操作

    类的成员对 String 对象执行序号或区域性敏感 (语言) 操作 String 。 序号运算作用于每个 Char 对象的数值。 区分区域性的操作作用于对象的值 String ,并考虑特定于区域性的大小写、排序、格式设置和分析规则。 区分区域性的操作在显式声明的区域性或隐式当前区域性的上下文中执行。 当对同一字符串执行这两种操作时,它们可能会产生非常不同的结果。

    .NET 还支持不区分区域性的语言字符串操作,方法是使用固定区域性 ( CultureInfo.InvariantCulture ) ,该区域性基于英语的区域性设置,与区域无关。 与其他 System.Globalization.CultureInfo 设置不同,固定区域性的设置保证在单个计算机上、从系统到系统以及跨 .NET 版本保持一致。 固定区域性可视为一种黑盒,可确保跨所有区域性进行字符串比较和排序的稳定性。

    如果应用程序对符号标识符(如文件名或命名管道)或持久化数据(如 XML 文件中基于文本的数据)做出安全决策,则操作应使用序号比较而不是区分区域性的比较。 这是因为区分区域性的比较可能会根据有效的区域性产生不同的结果,而序号比较仅取决于所比较字符的二进制值。

    执行字符串操作的大多数方法都包含一个重载,该重载具有 类型的 StringComparison 参数,这使你可以指定该方法执行的是序号操作还是区域性敏感运算。 通常,应调用此重载,以便明确方法调用的意图。 有关对字符串使用序号和区域性敏感操作的最佳做法和指南,请参阅 使用字符串的最佳做法

    大小写 分析和格式设置 比较和排序 以及 相等测试 的操作可以区分序号或区分区域性。 以下各节讨论每个操作类别。

    应始终调用使方法调用的意向清楚的方法重载。 例如,应 Compare(String, String, StringComparison) 调用 值为 comparisonType StringComparison.CurrentCulture 的 方法,而不是 Compare(String, String) 使用当前区域性的约定对两个字符串执行区分区域性的比较。 有关详细信息,请参阅 有关使用字符串的最佳实践

    可以从以下链接下载排序权重表,这是一组文本文件,其中包含有关排序和比较操作中使用的字符权重的信息:

  • Windows (.NET Framework 和 .NET Core) : 对权重表进行排序
  • Windows 10 2019 年 5 月更新或更高版本 (.NET 5+) 和 Linux 和 macOS (.NET Core 和 .NET 5+) : 默认 Unicode 排序规则元素表
  • 大小写规则确定如何更改 Unicode 字符的大写;例如,从小写到大写。 通常,在字符串比较之前执行大小写操作。 例如,可以将一个字符串转换为大写,以便它可以与另一个大写字符串进行比较。 可以通过调用 ToLower ToLowerInvariant 方法将字符串中的字符转换为小写,也可以通过调用 ToUpper ToUpperInvariant 方法将它们转换为大写。 此外,可以使用 TextInfo.ToTitleCase 方法将字符串转换为标题大小写。

    仅在 Linux 和 macOS 系统上运行的 .NET Core: C 和 Posix 区域性的排序规则行为始终区分大小写,因为这些区域性不使用预期的 Unicode 排序规则顺序。 建议使用除 C 或 Posix 以外的区域性执行区分区域性但不区分大小写的排序操作。

    大小写操作可以基于当前区域性、指定区域性或固定区域性的规则。 由于事例映射可能因使用的区域性而异,因此大小写操作的结果可能因区域性而异。 大小写的实际差异有三种:

  • 拉丁文大写字母 I (U+0049) 、拉丁文小写字母 I (U+0069) 、带上点的拉丁文大写字母 I (U+0130) 和拉丁文小写字母 DOTLESS I (U+0131) 。 在 tr-TR (土耳其语 (土耳其) ) 和 az-Latn-AZ (阿塞拜疆,拉丁文) 区域性中,在 tr、az 和 az-Latn 中性区域性中,拉丁文大写字母 I 的小写等效项为拉丁文小写字母 I,拉丁文小写字母 I 为带上 DOT 的拉丁文大写字母 I。 在所有其他区域性(包括固定区域性)中,拉丁文小写字母 I 和拉丁文大写字母 I 是小写和大写的等效项。

    以下示例演示了设计为阻止文件系统访问的字符串比较在依赖于区分区域性的大小写比较时如何失败。 (应使用固定区域性的大小写约定。)

    using System; using System.Globalization; using System.Threading; public class Example const string disallowed = "file"; public static void Main() IsAccessAllowed(@"FILE:\\\c:\users\user001\documents\FinancialInfo.txt"); private static void IsAccessAllowed(String resource) CultureInfo[] cultures = { CultureInfo.CreateSpecificCulture("en-US"), CultureInfo.CreateSpecificCulture("tr-TR") }; String scheme = null; int index = resource.IndexOfAny( new Char[] { '\\', '/' } ); if (index > 0) scheme = resource.Substring(0, index - 1); // Change the current culture and perform the comparison. foreach (var culture in cultures) { Thread.CurrentThread.CurrentCulture = culture; Console.WriteLine("Culture: {0}", CultureInfo.CurrentCulture.DisplayName); Console.WriteLine(resource); Console.WriteLine("Access allowed: {0}", ! String.Equals(disallowed, scheme, StringComparison.CurrentCultureIgnoreCase)); Console.WriteLine(); // The example displays the following output: // Culture: English (United States) // FILE:\\\c:\users\user001\documents\FinancialInfo.txt // Access allowed: False // Culture: Turkish (Turkey) // FILE:\\\c:\users\user001\documents\FinancialInfo.txt // Access allowed: True open System open System.Globalization open System.Threading let disallowed = "file" let isAccessAllowed (resource: string) = let cultures = [| CultureInfo.CreateSpecificCulture "en-US" CultureInfo.CreateSpecificCulture "tr-TR" |] let index = resource.IndexOfAny [| '\\'; '/' |] let scheme = if index > 0 then resource.Substring(0, index - 1) // Change the current culture and perform the comparison. for culture in cultures do Thread.CurrentThread.CurrentCulture <- culture printfn $"Culture: {CultureInfo.CurrentCulture.DisplayName}" printfn $"{resource}" printfn $"Access allowed: {String.Equals(disallowed, scheme, StringComparison.CurrentCultureIgnoreCase) |> not}" printfn "" isAccessAllowed @"FILE:\\\c:\users\user001\documents\FinancialInfo.txt" // The example displays the following output: // Culture: English (United States) // FILE:\\\c:\users\user001\documents\FinancialInfo.txt // Access allowed: False // Culture: Turkish (Turkey) // FILE:\\\c:\users\user001\documents\FinancialInfo.txt // Access allowed: True Imports System.Globalization Imports System.Threading Module Example Const disallowed = "file" Public Sub Main() IsAccessAllowed("FILE:\\\c:\users\user001\documents\FinancialInfo.txt") End Sub Private Sub IsAccessAllowed(resource As String) Dim cultures() As CultureInfo = { CultureInfo.CreateSpecificCulture("en-US"), CultureInfo.CreateSpecificCulture("tr-TR") } Dim scheme As String = Nothing Dim index As Integer = resource.IndexOfAny( {"\"c, "/"c }) If index > 0 Then scheme = resource.Substring(0, index - 1) ' Change the current culture and perform the comparison. For Each culture In cultures Thread.CurrentThread.CurrentCulture = culture Console.WriteLine("Culture: {0}", CultureInfo.CurrentCulture.DisplayName) Console.WriteLine(resource) Console.WriteLine("Access allowed: {0}", Not String.Equals(disallowed, scheme, StringComparison.CurrentCultureIgnoreCase)) Console.WriteLine() End Sub End Module ' The example displays the following output: ' Culture: English (United States) ' FILE:\\\c:\users\user001\documents\FinancialInfo.txt ' Access allowed: False ' Culture: Turkish (Turkey) ' FILE:\\\c:\users\user001\documents\FinancialInfo.txt ' Access allowed: True
  • 固定区域性与所有其他区域性之间的大小写映射差异。 在这些情况下,使用固定区域性的大小写规则将字符更改为大写或小写将返回相同的字符。 对于所有其他区域性,它将返回不同的字符。 下表列出了一些受影响的字符。

    如果更改为
  • ASCII 字符范围中双字母混合大小写对的大小写映射的差异。 在大多数区域性中,双字母混合大小写对等于等效的双字母大写或小写对。 对于以下区域性中的以下两个字母对来说,情况并非如此,因为在每种情况下,它们都与一个 digraph 进行比较:

  • hr-HR (克罗地亚 (克罗地亚) ) 区域性中的“lJ”和“nJ”。

  • cs-CZ 中的“cH” (捷克 (捷克共和国) ) 和 sk-SK (斯洛伐克 (斯洛伐克) ) 区域性。

  • da-DK 中的“aA” (丹麦语 (丹麦) ) 区域性。

  • hu-HU (匈牙利 (匈牙利) ) 区域性中的“cS”、“dZ”、“dZS”、“nY”、“sZ”、“tY”和“zS”。

  • es-ES_tradnl (西班牙语 (西班牙传统排序) ) 区域性中的“cH”和“lL”。

  • vi-VN (越南语 (越南) ) 区域性中的“cH”、“gI”、“kH”、“nG”、“nH”、“pH”、“qU'”、“tH”和“tR”。

    但是,遇到这些对的区域性敏感比较产生问题的情况并不常见,因为这些对在固定字符串或标识符中并不常见。

    以下示例演示了将字符串转换为大写时,不同区域性之间的大小写规则的一些差异。

    using namespace System; using namespace System::Globalization; using namespace System::IO; String^ ShowHexValue(String^ s); void main() StreamWriter^ sw = gcnew StreamWriter(".\\case.txt"); array<String^>^ words = gcnew array<String^> { L"file", L"sıfır", L"Dženana" }; array<CultureInfo^>^ cultures = gcnew array<CultureInfo^> { CultureInfo::InvariantCulture, gcnew CultureInfo("en-US"), gcnew CultureInfo("tr-TR") }; for each (String^ word in words) { sw->WriteLine("{0}:", word); for each (CultureInfo^ culture in cultures) { String^ name = String::IsNullOrEmpty(culture->Name) ? "Invariant" : culture->Name; String^ upperWord = word->ToUpper(culture); sw->WriteLine(" {0,10}: {1,7} {2, 38}", name, upperWord, ShowHexValue(upperWord)); sw->WriteLine(); sw->Close(); String^ ShowHexValue(String^ s) String^ retval = nullptr; for each (Char ch in s) { array<Byte>^ bytes = BitConverter::GetBytes(ch); retval += String::Format("{0:X2} {1:X2} ", bytes[1], bytes[0]); return retval; // The example displays the following output: // file: // Invariant: FILE 00 46 00 49 00 4C 00 45 // en-US: FILE 00 46 00 49 00 4C 00 45 // tr-TR: FİLE 00 46 01 30 00 4C 00 45 // sıfır: // Invariant: SıFıR 00 53 01 31 00 46 01 31 00 52 // en-US: SIFIR 00 53 00 49 00 46 00 49 00 52 // tr-TR: SIFIR 00 53 00 49 00 46 00 49 00 52 // Dženana: // Invariant: DžENANA 01 C5 00 45 00 4E 00 41 00 4E 00 41 // en-US: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41 // tr-TR: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41 using System; using System.Globalization; using System.IO; public class Example public static void Main() StreamWriter sw = new StreamWriter(@".\case.txt"); string[] words = { "file", "sıfır", "Dženana" }; CultureInfo[] cultures = { CultureInfo.InvariantCulture, new CultureInfo("en-US"), new CultureInfo("tr-TR") }; foreach (var word in words) { sw.WriteLine("{0}:", word); foreach (var culture in cultures) { string name = String.IsNullOrEmpty(culture.Name) ? "Invariant" : culture.Name; string upperWord = word.ToUpper(culture); sw.WriteLine(" {0,10}: {1,7} {2, 38}", name, upperWord, ShowHexValue(upperWord)); sw.WriteLine(); sw.Close(); private static string ShowHexValue(string s) string retval = null; foreach (var ch in s) { byte[] bytes = BitConverter.GetBytes(ch); retval += String.Format("{0:X2} {1:X2} ", bytes[1], bytes[0]); return retval; // The example displays the following output: // file: // Invariant: FILE 00 46 00 49 00 4C 00 45 // en-US: FILE 00 46 00 49 00 4C 00 45 // tr-TR: FİLE 00 46 01 30 00 4C 00 45 // sıfır: // Invariant: SıFıR 00 53 01 31 00 46 01 31 00 52 // en-US: SIFIR 00 53 00 49 00 46 00 49 00 52 // tr-TR: SIFIR 00 53 00 49 00 46 00 49 00 52 // Dženana: // Invariant: DžENANA 01 C5 00 45 00 4E 00 41 00 4E 00 41 // en-US: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41 // tr-TR: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41 open System open System.Globalization open System.IO let showHexValue (s: string) = let mutable retval = "" for ch in s do let bytes = BitConverter.GetBytes ch retval <- retval + String.Format("{0:X2} {1:X2} ", bytes[1], bytes[0]) retval use sw = new StreamWriter(@".\case.txt") let words = [| "file"; "sıfır"; "Dženana" |] let cultures = [| CultureInfo.InvariantCulture CultureInfo "en-US" CultureInfo "tr-TR" |] for word in words do sw.WriteLine("{0}:", word) for culture in cultures do let name = if String.IsNullOrEmpty culture.Name then "Invariant" else culture.Name let upperWord = word.ToUpper culture sw.WriteLine(" {0,10}: {1,7} {2, 38}", name, upperWord, showHexValue upperWord) sw.WriteLine() sw.Close() // The example displays the following output: // file: // Invariant: FILE 00 46 00 49 00 4C 00 45 // en-US: FILE 00 46 00 49 00 4C 00 45 // tr-TR: FİLE 00 46 01 30 00 4C 00 45 // sıfır: // Invariant: SıFıR 00 53 01 31 00 46 01 31 00 52 // en-US: SIFIR 00 53 00 49 00 46 00 49 00 52 // tr-TR: SIFIR 00 53 00 49 00 46 00 49 00 52 // Dženana: // Invariant: DžENANA 01 C5 00 45 00 4E 00 41 00 4E 00 41 // en-US: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41 // tr-TR: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41 Imports System.Globalization Imports System.IO Module Example Public Sub Main() Dim sw As New StreamWriter(".\case.txt") Dim words As String() = { "file", "sıfır", "Dženana" } Dim cultures() As CultureInfo = { CultureInfo.InvariantCulture, New CultureInfo("en-US"), New CultureInfo("tr-TR") } For Each word In words sw.WriteLine("{0}:", word) For Each culture In cultures Dim name As String = If(String.IsNullOrEmpty(culture.Name), "Invariant", culture.Name) Dim upperWord As String = word.ToUpper(culture) sw.WriteLine(" {0,10}: {1,7} {2, 38}", name, upperWord, ShowHexValue(upperWord)) sw.WriteLine() sw.Close() End Sub Private Function ShowHexValue(s As String) As String Dim retval As String = Nothing For Each ch In s Dim bytes() As Byte = BitConverter.GetBytes(ch) retval += String.Format("{0:X2} {1:X2} ", bytes(1), bytes(0)) Return retval End Function End Module ' The example displays the following output: ' file: ' Invariant: FILE 00 46 00 49 00 4C 00 45 ' en-US: FILE 00 46 00 49 00 4C 00 45 ' tr-TR: FİLE 00 46 01 30 00 4C 00 45 ' sıfır: ' Invariant: SıFıR 00 53 01 31 00 46 01 31 00 52 ' en-US: SIFIR 00 53 00 49 00 46 00 49 00 52 ' tr-TR: SIFIR 00 53 00 49 00 46 00 49 00 52 ' Dženana: ' Invariant: DžENANA 01 C5 00 45 00 4E 00 41 00 4E 00 41 ' en-US: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41 ' tr-TR: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41

    分析和格式设置

    格式设置和分析是反操作。 格式设置规则确定如何将值(如日期和时间或数字)转换为其字符串表示形式,而分析规则确定如何将字符串表示形式转换为日期和时间等值。 格式设置和分析规则都依赖于区域性约定。 以下示例说明了解释区域性特定日期字符串时可能出现的歧义。 如果不了解用于生成日期字符串的区域性约定,则无法知道 03/01/2011、3/1/2011 和 01/03/2011 表示 2011 年 1 月 3 日还是 2011 年 3 月 1 日。

    using namespace System; using namespace System::Globalization; void main() DateTime^ date = gcnew DateTime(2011, 3, 1); array<CultureInfo^>^ cultures = gcnew array<CultureInfo^> { CultureInfo::InvariantCulture, gcnew CultureInfo("en-US"), gcnew CultureInfo("fr-FR") }; for each (CultureInfo^ culture in cultures) Console::WriteLine("{0,-12} {1}", String::IsNullOrEmpty(culture->Name) ? "Invariant" : culture->Name, date->ToString("d", culture)); // The example displays the following output: // Invariant 03/01/2011 // en-US 3/1/2011 // fr-FR 01/03/2011 using System; using System.Globalization; public class Example public static void Main() DateTime date = new DateTime(2011, 3, 1); CultureInfo[] cultures = { CultureInfo.InvariantCulture, new CultureInfo("en-US"), new CultureInfo("fr-FR") }; foreach (var culture in cultures) Console.WriteLine("{0,-12} {1}", String.IsNullOrEmpty(culture.Name) ? "Invariant" : culture.Name, date.ToString("d", culture)); // The example displays the following output: // Invariant 03/01/2011 // en-US 3/1/2011 // fr-FR 01/03/2011 open System open System.Globalization let date = DateTime(2011, 3, 1) let cultures = [| CultureInfo.InvariantCulture CultureInfo "en-US" CultureInfo "fr-FR" |] for culture in cultures do printfn $"""{(if String.IsNullOrEmpty culture.Name then "Invariant" else culture.Name),-12} {date.ToString("d", culture)}""" // The example displays the following output: // Invariant 03/01/2011 // en-US 3/1/2011 // fr-FR 01/03/2011 Imports System.Globalization Module Example Public Sub Main() Dim dat As Date = #3/1/2011# Dim cultures() As CultureInfo = { CultureInfo.InvariantCulture, New CultureInfo("en-US"), New CultureInfo("fr-FR") } For Each culture In cultures Console.WriteLine("{0,-12} {1}", If(String.IsNullOrEmpty(culture.Name), "Invariant", culture.Name), dat.ToString("d", culture)) End Sub End Module ' The example displays the following output: ' Invariant 03/01/2011 ' en-US 3/1/2011 ' fr-FR 01/03/2011

    同样,如以下示例所示,单个字符串可以生成不同的日期,具体取决于在分析操作中使用其约定的区域性。

    using namespace System; using namespace System::Globalization; void main() String^ dateString = "07/10/2011"; array<CultureInfo^>^ cultures = gcnew array<CultureInfo^> { CultureInfo::InvariantCulture, CultureInfo::CreateSpecificCulture("en-GB"), CultureInfo::CreateSpecificCulture("en-US") }; Console::WriteLine("{0,-12} {1,10} {2,8} {3,8}\n", "Date String", "Culture", "Month", "Day"); for each (CultureInfo^ culture in cultures) { DateTime date = DateTime::Parse(dateString, culture); Console::WriteLine("{0,-12} {1,10} {2,8} {3,8}", dateString, String::IsNullOrEmpty(culture->Name) ? "Invariant" : culture->Name, date.Month, date.Day); // The example displays the following output: // Date String Culture Month Day // 07/10/2011 Invariant 7 10 // 07/10/2011 en-GB 10 7 // 07/10/2011 en-US 7 10 using System; using System.Globalization; public class Example public static void Main() string dateString = "07/10/2011"; CultureInfo[] cultures = { CultureInfo.InvariantCulture, CultureInfo.CreateSpecificCulture("en-GB"), CultureInfo.CreateSpecificCulture("en-US") }; Console.WriteLine("{0,-12} {1,10} {2,8} {3,8}\n", "Date String", "Culture", "Month", "Day"); foreach (var culture in cultures) { DateTime date = DateTime.Parse(dateString, culture); Console.WriteLine("{0,-12} {1,10} {2,8} {3,8}", dateString, String.IsNullOrEmpty(culture.Name) ? "Invariant" : culture.Name, date.Month, date.Day); // The example displays the following output: // Date String Culture Month Day // 07/10/2011 Invariant 7 10 // 07/10/2011 en-GB 10 7 // 07/10/2011 en-US 7 10 open System open System.Globalization let dateString = "07/10/2011" let cultures = [| CultureInfo.InvariantCulture CultureInfo.CreateSpecificCulture "en-GB" CultureInfo.CreateSpecificCulture "en-US" |] printfn $"""{"Date String",-12} {"Culture",10} {"Month",8} {"Day",8}\n""" for culture in cultures do let date = DateTime.Parse(dateString, culture) printfn $"""{dateString,-12} {(if String.IsNullOrEmpty culture.Name then "Invariant" else culture.Name),10} {date.Month,8} {date.Day,8}""" // The example displays the following output: // Date String Culture Month Day // 07/10/2011 Invariant 7 10 // 07/10/2011 en-GB 10 7 // 07/10/2011 en-US 7 10 Imports System.Globalization Module Example Public Sub Main() Dim dateString As String = "07/10/2011" Dim cultures() As CultureInfo = { CultureInfo.InvariantCulture, CultureInfo.CreateSpecificCulture("en-GB"), CultureInfo.CreateSpecificCulture("en-US") } Console.WriteLine("{0,-12} {1,10} {2,8} {3,8}", "Date String", "Culture", "Month", "Day") Console.WriteLine() For Each culture In cultures Dim dat As Date = DateTime.Parse(dateString, culture) Console.WriteLine("{0,-12} {1,10} {2,8} {3,8}", dateString, If(String.IsNullOrEmpty(culture.Name), "Invariant", culture.Name), dat.Month, dat.Day) End Sub End Module ' The example displays the following output: ' Date String Culture Month Day ' 07/10/2011 Invariant 7 10 ' 07/10/2011 en-GB 10 7 ' 07/10/2011 en-US 7 10

    字符串比较和排序

    字符串比较和排序的约定因区域性而异。 例如,排序顺序可能基于拼音或字符的视觉表示形式。 在东亚语言中,按文字的笔画和部首对字符进行排序。 排序也取决于字母表使用的排序语言和区域性。 例如,丹麦语有一个“Æ”字符,它在字母表中按“Z”排序。 此外,比较可以区分大小写或不区分大小写,大小写规则可能因区域性而异。 另一方面,序号比较在比较和排序字符串时使用字符串中各个字符的 Unicode 码位。

    排序规则确定 Unicode 字符的字母顺序以及两个字符串之间的比较方式。 例如, String.Compare(String, String, StringComparison) 方法基于 StringComparison 参数比较两个字符串。 如果参数值为 StringComparison.CurrentCulture ,则该方法执行使用当前区域性约定的语言比较;如果参数值为 StringComparison.Ordinal ,则该方法执行序号比较。 因此,如以下示例所示,如果当前区域性为美国英语,则对方法的第一次调用 String.Compare(String, String, StringComparison) (使用区分区域性的比较) 认为“a”小于“A”,但对同一方法的第二次调用 (使用序号比较) 认为“a”大于“A”。

    using namespace System; using namespace System::Globalization; using namespace System::Threading; void main() Thread::CurrentThread->CurrentCulture = CultureInfo::CreateSpecificCulture("en-US"); Console::WriteLine(String::Compare("A", "a", StringComparison::CurrentCulture)); Console::WriteLine(String::Compare("A", "a", StringComparison::Ordinal)); // The example displays the following output: // 1 // -32 using System; using System.Globalization; using System.Threading; public class Example public static void Main() Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US"); Console.WriteLine(String.Compare("A", "a", StringComparison.CurrentCulture)); Console.WriteLine(String.Compare("A", "a", StringComparison.Ordinal)); // The example displays the following output: // 1 // -32 open System open System.Globalization open System.Threading Thread.CurrentThread.CurrentCulture <- CultureInfo.CreateSpecificCulture "en-US" printfn $"""{String.Compare("A", "a", StringComparison.CurrentCulture)}""" printfn $"""{String.Compare("A", "a", StringComparison.Ordinal)}""" // The example displays the following output: // 1 // -32 Imports System.Globalization Imports System.Threading Module Example Public Sub Main() Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US") Console.WriteLine(String.Compare("A", "a", StringComparison.CurrentCulture)) Console.WriteLine(String.Compare("A", "a", StringComparison.Ordinal)) End Sub End Module ' The example displays the following output: ' 1 ' -32

    .NET 支持单词、字符串和序号排序规则:

  • 单词排序对某些非字母数字 Unicode 字符可能分配有特殊权重的字符串执行区分区域性的比较。 例如,为连字符 ( ) 可能分配了非常小的权重,以便“coop”和“co-op”在排序列表中彼此相邻显示。 有关使用单词排序规则比较两个字符串的方法列表 String ,请参阅 按类别划分的字符串操作 部分。

  • 字符串排序还会执行区分区域性的比较。 它类似于单词排序,只不过没有特殊情况,并且所有非字母数字符号都位于所有字母数字 Unicode 字符之前。 可以使用字符串排序规则来比较两个 options 字符串,方法是调用 CompareInfo.Compare 方法重载,这些重载具有提供值为 的参数 CompareOptions.StringSort 。 请注意,这是 .NET 提供的唯一方法,用于使用字符串排序规则比较两个字符串。

  • 序号排序基于字符串中每个 Char 对象的数值比较字符串。 序号比较自动区分大小写,因为字符的小写和大写版本具有不同的码位。 但是,如果大小写不重要,可以指定忽略大小写的序号比较。 这相当于使用固定区域性将字符串转换为大写,然后对结果执行序号比较。 有关使用序号排序规则比较两个字符串的方法列表 String ,请参阅 按类别划分的字符串操作 部分。

    区分区域性的比较是显式或隐式使用 CultureInfo 对象的任何比较,包括 由 CultureInfo.InvariantCulture 属性指定的固定区域性。 隐式区域性是当前区域性,由 Thread.CurrentCulture CultureInfo.CurrentCulture 属性指定。 字母字符的排序顺序 (,即属性返回 true Char.IsLetter 字符) 跨区域性。 可以通过向字符串比较方法(如 Compare(String, String, CultureInfo, CompareOptions) CultureInfo 提供 对象来指定使用特定区域性约定的区分区域性的比较。 可以通过向 方法的适当重载提供 StringComparison.CurrentCulture StringComparison.CurrentCultureIgnoreCase 或 枚举的任何成员 CompareOptions.Ordinal CompareOptions CompareOptions.OrdinalIgnoreCase ,指定使用当前区域性约定的区分区域性的 Compare 比较。 区分区域性的比较通常适用于排序,而序号比较则不适用于排序。 序号比较通常适用于确定两个字符串是否相等 (即确定标识) 而区分区域性的比较则不相等。

    以下示例说明了区分区域性的比较和序号比较之间的差异。 该示例使用序号比较和 da-DK 和 en-US 区域性的约定来计算三个字符串“Apple”、“Æble”和“AEble”, (每个字符串都是) 调用方法时 Compare 的默认区域性。 由于丹麦语将字符“Æ”视为单个字母,并在字母表中将其排序为“Z”,因此字符串“Æble”大于“Apple”。 但是,“Æble”不被视为等效于“AEble”,因此“Æble”也大于“AEble”。 en-US 区域性不包括字母“Æ”,但将其视为等效于“AE”,这解释了为什么“Æble”小于“Apple”但等于“AEble”。 另一方面,序号比较认为“Apple”小于“Æble”,而“Æble”大于“AEble”。

    using System; using System.Globalization; using System.Threading; public class CompareStringSample public static void Main() string str1 = "Apple"; string str2 = "Æble"; string str3 = "AEble"; // Set the current culture to Danish in Denmark. Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK"); Console.WriteLine("Current culture: {0}", CultureInfo.CurrentCulture.Name); Console.WriteLine("Comparison of {0} with {1}: {2}", str1, str2, String.Compare(str1, str2)); Console.WriteLine("Comparison of {0} with {1}: {2}\n", str2, str3, String.Compare(str2, str3)); // Set the current culture to English in the U.S. Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); Console.WriteLine("Current culture: {0}", CultureInfo.CurrentCulture.Name); Console.WriteLine("Comparison of {0} with {1}: {2}", str1, str2, String.Compare(str1, str2)); Console.WriteLine("Comparison of {0} with {1}: {2}\n", str2, str3, String.Compare(str2, str3)); // Perform an ordinal comparison. Console.WriteLine("Ordinal comparison"); Console.WriteLine("Comparison of {0} with {1}: {2}", str1, str2, String.Compare(str1, str2, StringComparison.Ordinal)); Console.WriteLine("Comparison of {0} with {1}: {2}", str2, str3, String.Compare(str2, str3, StringComparison.Ordinal)); // The example displays the following output: // Current culture: da-DK // Comparison of Apple with Æble: -1 // Comparison of Æble with AEble: 1 // Current culture: en-US // Comparison of Apple with Æble: 1 // Comparison of Æble with AEble: 0 // Ordinal comparison // Comparison of Apple with Æble: -133 // Comparison of Æble with AEble: 133 open System open System.Globalization open System.Threading let str1 = "Apple" let str2 = "Æble" let str3 = "AEble" // Set the current culture to Danish in Denmark. Thread.CurrentThread.CurrentCulture <- CultureInfo "da-DK" printfn $"Current culture: {CultureInfo.CurrentCulture.Name}" printfn $"Comparison of {str1} with {str2}: {String.Compare(str1, str2)}" printfn $"Comparison of {str2} with {str3}: {String.Compare(str2, str3)}\n" // Set the current culture to English in the U.S. Thread.CurrentThread.CurrentCulture <- CultureInfo "en-US" printfn $"Current culture: {CultureInfo.CurrentCulture.Name}" printfn $"Comparison of {str1} with {str2}: {String.Compare(str1, str2)}" printfn $"Comparison of {str2} with {str3}: {String.Compare(str2, str3)}\n" // Perform an ordinal comparison. printfn "Ordinal comparison" printfn $"Comparison of {str1} with {str2}: {String.Compare(str1, str2, StringComparison.Ordinal)}" printfn $"Comparison of {str2} with {str3}: {String.Compare(str2, str3, StringComparison.Ordinal)}" // The example displays the following output: // Current culture: da-DK // Comparison of Apple with Æble: -1 // Comparison of Æble with AEble: 1 // Current culture: en-US // Comparison of Apple with Æble: 1 // Comparison of Æble with AEble: 0 // Ordinal comparison // Comparison of Apple with Æble: -133 // Comparison of Æble with AEble: 133 Imports System.Globalization Imports System.Threading Public Module Example Public Sub Main() Dim str1 As String = "Apple" Dim str2 As String = "Æble" Dim str3 As String = "AEble" ' Set the current culture to Danish in Denmark. Thread.CurrentThread.CurrentCulture = New CultureInfo("da-DK") Console.WriteLine("Current culture: {0}", CultureInfo.CurrentCulture.Name) Console.WriteLine("Comparison of {0} with {1}: {2}", str1, str2, String.Compare(str1, str2)) Console.WriteLine("Comparison of {0} with {1}: {2}", str2, str3, String.Compare(str2, str3)) Console.WriteLine() ' Set the current culture to English in the U.S. Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US") Console.WriteLine("Current culture: {0}", CultureInfo.CurrentCulture.Name) Console.WriteLine("Comparison of {0} with {1}: {2}", str1, str2, String.Compare(str1, str2)) Console.WriteLine("Comparison of {0} with {1}: {2}", str2, str3, String.Compare(str2, str3)) Console.WriteLine() ' Perform an ordinal comparison. Console.WriteLine("Ordinal comparison") Console.WriteLine("Comparison of {0} with {1}: {2}", str1, str2, String.Compare(str1, str2, StringComparison.Ordinal)) Console.WriteLine("Comparison of {0} with {1}: {2}", str2, str3, String.Compare(str2, str3, StringComparison.Ordinal)) End Sub End Module ' The example displays the following output: ' Current culture: da-DK ' Comparison of Apple with Æble: -1 ' Comparison of Æble with AEble: 1 ' Current culture: en-US ' Comparison of Apple with Æble: 1 ' Comparison of Æble with AEble: 0 ' Ordinal comparison ' Comparison of Apple with Æble: -133 ' Comparison of Æble with AEble: 133

    使用以下一般准则选择适当的排序或字符串比较方法:

  • 如果希望根据用户的区域性对字符串进行排序,则应根据当前区域性的约定对字符串进行排序。 如果用户的区域性发生更改,排序字符串的顺序也会相应更改。 例如,同义词库应用程序应始终根据用户的区域性对字词进行排序。

  • 如果希望根据特定区域性的约定对字符串进行排序,则应通过向比较方法提供 CultureInfo 表示该区域性的 对象来对字符串进行排序。 例如,在设计用于教授学生特定语言的应用程序中,你希望根据讲该语言的其中一种区域性的约定对字符串进行排序。

  • 如果希望字符串的顺序在各个区域性中保持不变,则应根据固定区域性的约定对字符串进行排序,或使用序号比较。 例如,可以使用序号排序来组织文件、进程、互斥体或命名管道的名称。

  • 对于涉及安全决策 ((例如用户名是否有效) )的比较,应始终通过调用 方法的 Equals 重载来执行相等性序号测试。

    字符串比较中使用的区分区域性的排序和大小写规则取决于 .NET 的版本。 在 .NET Core 上,字符串比较取决于基础操作系统支持的 Unicode 标准版本。 在 windows 8 或更高版本上运行的 .NET Framework 4.5 及更高版本中,排序、大小写、规范化和 Unicode 字符信息符合 Unicode 6.0 标准。 在其他 Windows 操作系统上,它们符合 Unicode 5.0 标准。

    有关单词、字符串和序号排序规则的详细信息,请参阅 主题 System.Globalization.CompareOptions 。 有关何时使用每个规则的其他建议,请参阅 使用字符串的最佳做法

    通常,不会直接调用字符串比较方法(如 Compare )来确定字符串的排序顺序。 相反,比较方法是通过排序方法(如 Array.Sort List<T>.Sort )调用的。 以下示例执行四种不同的排序操作, (使用当前区域性的单词排序、使用固定区域性的单词排序、使用固定区域性的序号排序和使用固定区域性) 字符串排序,而不显式调用字符串比较方法,尽管它们确实指定了要使用的比较类型。 请注意,每种类型的排序在其数组中生成唯一的字符串排序。

    using namespace System; using namespace System::Collections; using namespace System::Collections::Generic; using namespace System::Globalization; // IComparer<String> implementation to perform string sort. ref class SCompare : System::Collections::Generic::IComparer<String^> public: SCompare() {}; virtual int Compare(String^ x, String^ y) return CultureInfo::CurrentCulture->CompareInfo->Compare(x, y, CompareOptions::StringSort); void main() array<String^>^ strings = gcnew array<String^> { "coop", "co-op", "cooperative", L"co\x00ADoperative", L"c�ur", "coeur" }; // Perform a word sort using the current (en-US) culture. array<String^>^ current = gcnew array<String^>(strings->Length); strings->CopyTo(current, 0); Array::Sort(current, StringComparer::CurrentCulture); // Perform a word sort using the invariant culture. array<String^>^ invariant = gcnew array<String^>(strings->Length); strings->CopyTo(invariant, 0); Array::Sort(invariant, StringComparer::InvariantCulture); // Perform an ordinal sort. array<String^>^ ordinal = gcnew array<String^>(strings->Length); strings->CopyTo(ordinal, 0); Array::Sort(ordinal, StringComparer::Ordinal); // Perform a string sort using the current culture. array<String^>^ stringSort = gcnew array<String^>(strings->Length); strings->CopyTo(stringSort, 0); Array::Sort(stringSort, gcnew SCompare()); // Display array values Console::WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}\n", "Original", "Word Sort", "Invariant Word", "Ordinal Sort", "String Sort"); for (int ctr = 0; ctr < strings->Length; ctr++) Console::WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}", strings[ctr], current[ctr], invariant[ctr], ordinal[ctr], stringSort[ctr] ); // The example displays the following output: // Original Word Sort Invariant Word Ordinal Sort String Sort // coop c�ur c�ur co-op co-op // co-op coeur coeur coeur c�ur // cooperative coop coop coop coeur // co�operative co-op co-op cooperative coop // c�ur cooperative cooperative co�operative cooperative // coeur co�operative co�operative c�ur co�operative using System; using System.Collections; using System.Collections.Generic; using System.Globalization; public class Example public static void Main() string[] strings = { "coop", "co-op", "cooperative", "co\u00ADoperative", "cœur", "coeur" }; // Perform a word sort using the current (en-US) culture. string[] current = new string[strings.Length]; strings.CopyTo(current, 0); Array.Sort(current, StringComparer.CurrentCulture); // Perform a word sort using the invariant culture. string[] invariant = new string[strings.Length]; strings.CopyTo(invariant, 0); Array.Sort(invariant, StringComparer.InvariantCulture); // Perform an ordinal sort. string[] ordinal = new string[strings.Length]; strings.CopyTo(ordinal, 0); Array.Sort(ordinal, StringComparer.Ordinal); // Perform a string sort using the current culture. string[] stringSort = new string[strings.Length]; strings.CopyTo(stringSort, 0); Array.Sort(stringSort, new SCompare()); // Display array values Console.WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}\n", "Original", "Word Sort", "Invariant Word", "Ordinal Sort", "String Sort"); for (int ctr = 0; ctr < strings.Length; ctr++) Console.WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}", strings[ctr], current[ctr], invariant[ctr], ordinal[ctr], stringSort[ctr] ); // IComparer<String> implementation to perform string sort. internal class SCompare : IComparer<String> public int Compare(string x, string y) return CultureInfo.CurrentCulture.CompareInfo.Compare(x, y, CompareOptions.StringSort); // The example displays the following output: // Original Word Sort Invariant Word Ordinal Sort String Sort // coop cœur cœur co-op co-op // co-op coeur coeur coeur cœur // cooperative coop coop coop coeur // co­operative co-op co-op cooperative coop // cœur cooperative cooperative co­operative cooperative // coeur co­operative co­operative cœur co­operative open System open System.Collections.Generic open System.Globalization // IComparer<String> implementation to perform string sort using an F# object expression. let scompare = { new IComparer<String> with member _.Compare(x, y) = CultureInfo.CurrentCulture.CompareInfo.Compare(x, y, CompareOptions.StringSort) } let strings = [| "coop"; "co-op"; "cooperative"; "co\u00ADoperative"; "cœur"; "coeur" |] // Perform a word sort using the current (en-US) culture. let current = Array.copy strings Array.Sort(current, StringComparer.CurrentCulture) // Perform a word sort using the invariant culture. let invariant = Array.copy strings Array.Sort(invariant, StringComparer.InvariantCulture) // Perform an ordinal sort. let ordinal = Array.copy strings Array.Sort(ordinal, StringComparer.Ordinal) // Perform a string sort using the current culture. let stringSort = Array.copy strings Array.Sort(stringSort, scompare) // Display array values printfn "%13s %13s %15s %13s %13s\n" "Original" "Word Sort" "Invariant Word" "Ordinal Sort" "String Sort" for i = 0 to strings.Length - 1 do printfn "%13s %13s %15s %13s %13s\n" strings[i] current[i] invariant[i] ordinal[i] stringSort[i] // The example displays the following output: // Original Word Sort Invariant Word Ordinal Sort String Sort // coop cœur cœur co-op co-op // co-op coeur coeur coeur cœur // cooperative coop coop coop coeur // co­operative co-op co-op cooperative coop // cœur cooperative cooperative co­operative cooperative // coeur co­operative co­operative cœur co­operative Imports System.Collections Imports System.Collections.Generic Imports System.Globalization Module Example Public Sub Main() Dim strings() As String = { "coop", "co-op", "cooperative", "co" + ChrW(&h00AD) + "operative", "cœur", "coeur" } ' Perform a word sort using the current (en-US) culture. Dim current(strings.Length - 1) As String strings.CopyTo(current, 0) Array.Sort(current, StringComparer.CurrentCulture) ' Perform a word sort using the invariant culture. Dim invariant(strings.Length - 1) As String strings.CopyTo(invariant, 0) Array.Sort(invariant, StringComparer.InvariantCulture) ' Perform an ordinal sort. Dim ordinal(strings.Length - 1) As String strings.CopyTo(ordinal, 0) Array.Sort(ordinal, StringComparer.Ordinal) ' Perform a string sort using the current culture. Dim stringSort(strings.Length - 1) As String strings.CopyTo(stringSort, 0) Array.Sort(stringSort, new SCompare()) ' Display array values Console.WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}", "Original", "Word Sort", "Invariant Word", "Ordinal Sort", "String Sort") Console.WriteLine() For ctr As Integer = 0 To strings.Length - 1 Console.WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}", strings(ctr), current(ctr), invariant(ctr), ordinal(ctr), stringSort(ctr)) End Sub End Module ' IComparer<String> implementation to perform string sort. Friend Class SCompare : Implements IComparer(Of String) Public Function Compare(x As String, y As String) As Integer _ Implements IComparer(Of String).Compare Return CultureInfo.CurrentCulture.CompareInfo.Compare(x, y, CompareOptions.StringSort) End Function End Class ' The example displays the following output: ' Original Word Sort Invariant Word Ordinal Sort String Sort ' coop cœur cœur co-op co-op ' co-op coeur coeur coeur cœur ' cooperative coop coop coop coeur ' co­operative co-op co-op cooperative coop ' cœur cooperative cooperative co­operative cooperative ' coeur co­operative co­operative cœur co­operative

    在内部,.NET 使用排序键来支持区分区域性的字符串比较。 对于字符串中的每个字符,都赋予若干类排序权重,包括字母、大小写和变音符。 由 SortKey 类表示的排序键为特定字符串提供这些权重的存储库。 如果应用对同一组字符串执行大量搜索或排序操作,则可以通过为其使用的所有字符串生成和存储排序键来提高其性能。 如果需要排序或比较操作,请使用排序键而不是字符串。 有关更多信息,请参见 SortKey 类。

    如果未指定字符串比较约定,排序方法(如 ) Array.Sort(Array) 对字符串执行区分区域性、区分大小写的排序。 下面的示例演示更改当前区域性如何影响数组中已排序字符串的顺序。 它创建一个包含三个字符串的数组。 首先,它将 System.Threading.Thread.CurrentThread.CurrentCulture 属性设置为 en-US,并调用 Array.Sort(Array) 方法。 生成的排序顺序基于英语 (美国) 区域性的排序约定。 接着,此示例将 System.Threading.Thread.CurrentThread.CurrentCulture 属性设置为 da-DK 并再次调用 Array.Sort 方法。 请注意,最终排序顺序与使用 en-US 时的结果不一样,因为这次使用的是针对丹麦语(丹麦)的排序约定。

    using System; using System.Globalization; using System.Threading; public class ArraySort public static void Main(String[] args) // Create and initialize a new array to store the strings. string[] stringArray = { "Apple", "Æble", "Zebra"}; // Display the values of the array. Console.WriteLine( "The original string array:"); PrintIndexAndValues(stringArray); // Set the CurrentCulture to "en-US". Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); // Sort the values of the array. Array.Sort(stringArray); // Display the values of the array. Console.WriteLine("After sorting for the culture \"en-US\":"); PrintIndexAndValues(stringArray); // Set the CurrentCulture to "da-DK". Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK"); // Sort the values of the Array. Array.Sort(stringArray); // Display the values of the array. Console.WriteLine("After sorting for the culture \"da-DK\":"); PrintIndexAndValues(stringArray); public static void PrintIndexAndValues(string[] myArray) for (int i = myArray.GetLowerBound(0); i <= myArray.GetUpperBound(0); i++ ) Console.WriteLine("[{0}]: {1}", i, myArray[i]); Console.WriteLine(); // The example displays the following output: // The original string array: // [0]: Apple // [1]: Æble // [2]: Zebra // After sorting for the "en-US" culture: // [0]: Æble // [1]: Apple // [2]: Zebra // After sorting for the culture "da-DK": // [0]: Apple // [1]: Zebra // [2]: Æble open System open System.Globalization open System.Threading let printIndexAndValues (myArray: string[]) = for i = myArray.GetLowerBound 0 to myArray.GetUpperBound 0 do printfn $"[{i}]: {myArray[i]}" printfn "" // Create and initialize a new array to store the strings. let stringArray = [| "Apple"; "Æble"; "Zebra" |] // Display the values of the array. printfn "The original string array:" printIndexAndValues stringArray // Set the CurrentCulture to "en-US". Thread.CurrentThread.CurrentCulture <- CultureInfo "en-US" // Sort the values of the array. Array.Sort stringArray // Display the values of the array. printfn "After sorting for the culture \"en-US\":" printIndexAndValues stringArray // Set the CurrentCulture to "da-DK". Thread.CurrentThread.CurrentCulture <- CultureInfo "da-DK" // Sort the values of the Array. Array.Sort stringArray // Display the values of the array. printfn "After sorting for the culture \"da-DK\":" printIndexAndValues stringArray // The example displays the following output: // The original string array: // [0]: Apple // [1]: Æble // [2]: Zebra // After sorting for the "en-US" culture: // [0]: Æble // [1]: Apple // [2]: Zebra // After sorting for the culture "da-DK": // [0]: Apple // [1]: Zebra // [2]: Æble Imports System.Globalization Imports System.IO Imports System.Threading Public Class TextToFile Public Shared Sub Main() ' Creates and initializes a new array to store ' these date/time objects. Dim stringArray() As String = { "Apple", "Æble", "Zebra"} ' Displays the values of the array. Console.WriteLine("The original string array:") PrintIndexAndValues(stringArray) ' Set the CurrentCulture to "en-US". Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US") ' Sort the values of the Array. Array.Sort(stringArray) ' Display the values of the array. Console.WriteLine("After sorting for the ""en-US"" culture:") PrintIndexAndValues(stringArray) ' Set the CurrentCulture to "da-DK". Thread.CurrentThread.CurrentCulture = New CultureInfo("da-DK") ' Sort the values of the Array. Array.Sort(stringArray) ' Displays the values of the Array. Console.WriteLine("After sorting for the culture ""da-DK"":") PrintIndexAndValues(stringArray) End Sub Public Shared Sub PrintIndexAndValues(myArray() As String) For i As Integer = myArray.GetLowerBound(0) To myArray.GetUpperBound(0) Console.WriteLine("[{0}]: {1}", i, myArray(i)) Console.WriteLine() End Sub End Class ' The example displays the following output: ' The original string array: ' [0]: Apple ' [1]: Æble ' [2]: Zebra ' After sorting for the "en-US" culture: ' [0]: Æble ' [1]: Apple ' [2]: Zebra ' After sorting for the culture "da-DK": ' [0]: Apple ' [1]: Zebra ' [2]: Æble

    如果比较字符串的主要目的是确定它们是否相等,则应调用 String.Equals 方法。 通常,应使用 Equals 来执行序号比较。 方法 String.Compare 主要用于对字符串进行排序。

    字符串搜索方法(如 String.StartsWith String.IndexOf )还可以执行区分区域性或序号字符串的比较。 下面的示例演示使用 IndexOf 方法进行序号和区分区域性的比较之间的差异。 区分区域性的搜索,其中当前区域性为英语 (美国) 将子字符串“oe”视为与连字“œ”匹配。 由于 U+00AD) (软连字符是零宽度字符,因此搜索会将软连字符视为等效 Empty 项,并在字符串开头查找匹配项。 另一方面,序号搜索在任一情况下都找不到匹配项。

    using namespace System; void FindInString(String^ s, String^ substring, StringComparison options); void main() // Search for "oe" and "�u" in "�ufs" and "oeufs". String^ s1 = L"�ufs"; String^ s2 = L"oeufs"; FindInString(s1, "oe", StringComparison::CurrentCulture); FindInString(s1, "oe", StringComparison::Ordinal); FindInString(s2, "�u", StringComparison::CurrentCulture); FindInString(s2, "�u", StringComparison::Ordinal); Console::WriteLine(); String^ s3 = L"co\x00ADoperative"; FindInString(s3, L"\x00AD", StringComparison::CurrentCulture); FindInString(s3, L"\x00AD", StringComparison::Ordinal); void FindInString(String^ s, String^ substring, StringComparison options) int result = s->IndexOf(substring, options); if (result != -1) Console::WriteLine("'{0}' found in {1} at position {2}", substring, s, result); Console::WriteLine("'{0}' not found in {1}", substring, s); // The example displays the following output: // 'oe' found in oufs at position 0 // 'oe' not found in oufs // 'ou' found in oeufs at position 0 // 'ou' not found in oeufs // '-' found in co-operative at position 0 // '-' found in co-operative at position 2 using System; public class Example public static void Main() // Search for "oe" and "œu" in "œufs" and "oeufs". string s1 = "œufs"; string s2 = "oeufs"; FindInString(s1, "oe", StringComparison.CurrentCulture); FindInString(s1, "oe", StringComparison.Ordinal); FindInString(s2, "œu", StringComparison.CurrentCulture); FindInString(s2, "œu", StringComparison.Ordinal); Console.WriteLine(); string s3 = "co\u00ADoperative"; FindInString(s3, "\u00AD", StringComparison.CurrentCulture); FindInString(s3, "\u00AD", StringComparison.Ordinal); private static void FindInString(string s, string substring, StringComparison options) int result = s.IndexOf(substring, options); if (result != -1) Console.WriteLine("'{0}' found in {1} at position {2}", substring, s, result); Console.WriteLine("'{0}' not found in {1}", substring, s); // The example displays the following output: // 'oe' found in œufs at position 0 // 'oe' not found in œufs // 'œu' found in oeufs at position 0 // 'œu' not found in oeufs // '­' found in co­operative at position 0 // '­' found in co­operative at position 2 open System let findInString (s: string) (substring: string) (options: StringComparison) = let result = s.IndexOf(substring, options) if result <> -1 then printfn $"'{substring}' found in {s} at position {result}" printfn $"'{substring}' not found in {s}" // Search for "oe" and "œu" in "œufs" and "oeufs". let s1 = "œufs" let s2 = "oeufs" findInString s1 "oe" StringComparison.CurrentCulture findInString s1 "oe" StringComparison.Ordinal findInString s2 "œu" StringComparison.CurrentCulture findInString s2 "œu" StringComparison.Ordinal printfn "" let s3 = "co\u00ADoperative" findInString s3 "\u00AD" StringComparison.CurrentCulture findInString s3 "\u00AD" StringComparison.Ordinal // The example displays the following output: // 'oe' found in œufs at position 0 // 'oe' not found in œufs // 'œu' found in oeufs at position 0 // 'œu' not found in oeufs // '­' found in co­operative at position 0 // '­' found in co­operative at position 2 Module Example Public Sub Main() ' Search for "oe" and "œu" in "œufs" and "oeufs". Dim s1 As String = "œufs" Dim s2 As String = "oeufs" FindInString(s1, "oe", StringComparison.CurrentCulture) FindInString(s1, "oe", StringComparison.Ordinal) FindInString(s2, "œu", StringComparison.CurrentCulture) FindInString(s2, "œu", StringComparison.Ordinal) Console.WriteLine() Dim softHyphen As String = ChrW(&h00AD) Dim s3 As String = "co" + softHyphen + "operative" FindInString(s3, softHyphen, StringComparison.CurrentCulture) FindInString(s3, softHyphen, StringComparison.Ordinal) End Sub Private Sub FindInString(s As String, substring As String, options As StringComparison) Dim result As Integer = s.IndexOf(substring, options) If result <> -1 Console.WriteLine("'{0}' found in {1} at position {2}", substring, s, result) Console.WriteLine("'{0}' not found in {1}", substring, s) End If End Sub End Module ' The example displays the following output: ' 'oe' found in œufs at position 0 ' 'oe' not found in œufs ' 'œu' found in oeufs at position 0 ' 'œu' not found in oeufs ' '­' found in co­operative at position 0 ' '­' found in co­operative at position 2

    在字符串中搜索

    字符串搜索方法(如 String.StartsWith String.IndexOf )还可以执行区分区域性或序号字符串比较,以确定是否在指定的字符串中找到字符或子字符串。

    类中 String 搜索单个字符(如 IndexOf 方法)或一组字符之一(如 IndexOfAny 方法)的搜索方法都执行序号搜索。 若要对字符执行区分区域性的搜索,必须调用 CompareInfo CompareInfo.LastIndexOf(String, Char) CompareInfo.IndexOf(String, Char) 方法。 请注意,使用序号和区分区域性的比较来搜索字符的结果可能大不相同。 例如,搜索预编译的 Unicode 字符(如连字“Æ” (U+00C6) ),可能与正确序列中出现的任何组件匹配,例如“AE” (U+041U+0045) ,具体取决于区域性。 以下示例说明了搜索单个字符时 和 String.IndexOf(Char) CompareInfo.IndexOf(String, Char) 方法之间的差异。 使用 en-US 区域性的约定时,在字符串“aerial”中找到连字“æ” (U+00E6) ,但在使用 da-DK 区域性的约定或执行序号比较时则找不到。

    using System; using System.Globalization; public class Example public static void Main() String[] cultureNames = { "da-DK", "en-US" }; CompareInfo ci; String str = "aerial"; Char ch = 'æ'; // U+00E6 Console.Write("Ordinal comparison -- "); Console.WriteLine("Position of '{0}' in {1}: {2}", ch, str, str.IndexOf(ch)); foreach (var cultureName in cultureNames) { ci = CultureInfo.CreateSpecificCulture(cultureName).CompareInfo; Console.Write("{0} cultural comparison -- ", cultureName); Console.WriteLine("Position of '{0}' in {1}: {2}", ch, str, ci.IndexOf(str, ch)); // The example displays the following output: // Ordinal comparison -- Position of 'æ' in aerial: -1 // da-DK cultural comparison -- Position of 'æ' in aerial: -1 // en-US cultural comparison -- Position of 'æ' in aerial: 0 open System.Globalization let cultureNames = [| "da-DK"; "en-US" |] let str = "aerial" let ch = 'æ' // U+00E6 printf "Ordinal comparison -- " printfn $"Position of '{ch}' in {str}: {str.IndexOf ch}" for cultureName in cultureNames do let ci = CultureInfo.CreateSpecificCulture(cultureName).CompareInfo printf $"{cultureName} cultural comparison -- " printfn $"Position of '{ch}' in {str}: {ci.IndexOf(str, ch)}" // The example displays the following output: // Ordinal comparison -- Position of 'æ' in aerial: -1 // da-DK cultural comparison -- Position of 'æ' in aerial: -1 // en-US cultural comparison -- Position of 'æ' in aerial: 0 Imports System.Globalization Module Example Public Sub Main() Dim cultureNames() As String = { "da-DK", "en-US" } Dim ci As CompareInfo Dim str As String = "aerial" Dim ch As Char = "æ"c ' U+00E6 Console.Write("Ordinal comparison -- ") Console.WriteLine("Position of '{0}' in {1}: {2}", ch, str, str.IndexOf(ch)) For Each cultureName In cultureNames ci = CultureInfo.CreateSpecificCulture(cultureName).CompareInfo Console.Write("{0} cultural comparison -- ", cultureName) Console.WriteLine("Position of '{0}' in {1}: {2}", ch, str, ci.IndexOf(str, ch)) End Sub End Module ' The example displays the following output: ' Ordinal comparison -- Position of 'æ' in aerial: -1 ' da-DK cultural comparison -- Position of 'æ' in aerial: -1 ' en-US cultural comparison -- Position of 'æ' in aerial: 0

    另一方面, String 如果搜索选项不是由 类型的 StringComparison 参数显式指定,则搜索字符串而不是字符的类方法将执行区分区域性的搜索。 唯一的例外是 Contains ,它执行序号搜索。

    测试相等性

    String.Compare 使用 方法确定排序顺序中两个字符串的关系。 通常,这是一个区分区域性的操作。 相反,调用 String.Equals 方法以测试相等性。 由于相等性测试通常会将用户输入与某些已知字符串(例如有效的用户名、密码或文件系统路径)进行比较,因此它通常是一个序号操作。

    可以通过调用 String.Compare 方法并确定返回值是否为零来测试相等性。 但是,不建议这种做法。 若要确定两个字符串是否相等,应调用 方法的重载之 String.Equals 一。 要调用的首选重载是实例 Equals(String, StringComparison) 方法或静态 Equals(String, String, StringComparison) 方法,因为这两种方法都包含显式 System.StringComparison 指定比较类型的参数。

    以下示例说明了在应改用序号比较时执行区分区域性的相等性比较的危险。 在这种情况下,代码的目的是通过对 URL 的开头与字符串“FILE://”不区分大小写的比较,禁止从以“FILE://”或“file://”开头的 URL 访问文件系统。 但是,如果在以“file://”开头的 URL 上使用土耳其语 (土耳其) 区域性执行区分区域性的比较,则相等性比较将失败,因为小写字母“i”的土耳其大写等效项是“ー”而不是“I”。 因此,无意中允许文件系统访问。 另一方面,如果执行序号比较,则相等性比较成功,并拒绝文件系统访问。

    using namespace System; using namespace System::Globalization; using namespace System::Threading; bool TestForEquality(String^ str, StringComparison cmp); void main() Thread::CurrentThread->CurrentCulture = CultureInfo::CreateSpecificCulture("tr-TR"); String^ filePath = "file://c:/notes.txt"; Console::WriteLine("Culture-sensitive test for equality:"); if (! TestForEquality(filePath, StringComparison::CurrentCultureIgnoreCase)) Console::WriteLine("Access to {0} is allowed.", filePath); Console::WriteLine("Access to {0} is not allowed.", filePath); Console::WriteLine("\nOrdinal test for equality:"); if (! TestForEquality(filePath, StringComparison::OrdinalIgnoreCase)) Console::WriteLine("Access to {0} is allowed.", filePath); Console::WriteLine("Access to {0} is not allowed.", filePath); bool TestForEquality(String^ str, StringComparison cmp) int position = str->IndexOf("://"); if (position < 0) return false; String^ substring = str->Substring(0, position); return substring->Equals("FILE", cmp); // The example displays the following output: // Culture-sensitive test for equality: // Access to file://c:/notes.txt is allowed. // Ordinal test for equality: // Access to file://c:/notes.txt is not allowed. using System; using System.Globalization; using System.Threading; public class Example public static void Main() Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("tr-TR"); string filePath = "file://c:/notes.txt"; Console.WriteLine("Culture-sensitive test for equality:"); if (! TestForEquality(filePath, StringComparison.CurrentCultureIgnoreCase)) Console.WriteLine("Access to {0} is allowed.", filePath); Console.WriteLine("Access to {0} is not allowed.", filePath); Console.WriteLine("\nOrdinal test for equality:"); if (! TestForEquality(filePath, StringComparison.OrdinalIgnoreCase)) Console.WriteLine("Access to {0} is allowed.", filePath); Console.WriteLine("Access to {0} is not allowed.", filePath); private static bool TestForEquality(string str, StringComparison cmp) int position = str.IndexOf("://"); if (position < 0) return false; string substring = str.Substring(0, position); return substring.Equals("FILE", cmp); // The example displays the following output: // Culture-sensitive test for equality: // Access to file://c:/notes.txt is allowed. // Ordinal test for equality: // Access to file://c:/notes.txt is not allowed. open System open System.Globalization open System.Threading let testForEquality (str: string) (cmp: StringComparison) = let position = str.IndexOf "://" if position < 0 then false let substring = str.Substring(0, position) substring.Equals("FILE", cmp) Thread.CurrentThread.CurrentCulture <- CultureInfo.CreateSpecificCulture "tr-TR" let filePath = "file://c:/notes.txt" printfn "Culture-sensitive test for equality:" if not (testForEquality filePath StringComparison.CurrentCultureIgnoreCase) then printfn $"Access to {filePath} is allowed." printfn $"Access to {filePath} is not allowed." printfn "\nOrdinal test for equality:" if not (testForEquality filePath StringComparison.OrdinalIgnoreCase) then printfn $"Access to {filePath} is allowed." printfn $"Access to {filePath} is not allowed." // The example displays the following output: // Culture-sensitive test for equality: // Access to file://c:/notes.txt is allowed. // Ordinal test for equality: // Access to file://c:/notes.txt is not allowed. Imports System.Globalization Imports System.Threading Module Example Public Sub Main() Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("tr-TR") Dim filePath As String = "file://c:/notes.txt" Console.WriteLine("Culture-sensitive test for equality:") If Not TestForEquality(filePath, StringComparison.CurrentCultureIgnoreCase) Then Console.WriteLine("Access to {0} is allowed.", filePath) Console.WriteLine("Access to {0} is not allowed.", filePath) End If Console.WriteLine() Console.WriteLine("Ordinal test for equality:") If Not TestForEquality(filePath, StringComparison.OrdinalIgnoreCase) Then Console.WriteLine("Access to {0} is allowed.", filePath) Console.WriteLine("Access to {0} is not allowed.", filePath) End If End Sub Private Function TestForEquality(str As String, cmp As StringComparison) As Boolean Dim position As Integer = str.IndexOf("://") If position < 0 Then Return False Dim substring As String = str.Substring(0, position) Return substring.Equals("FILE", cmp) End Function End Module ' The example displays the following output: ' Culture-sensitive test for equality: ' Access to file://c:/notes.txt is allowed. ' Ordinal test for equality: ' Access to file://c:/notes.txt is not allowed.

    某些 Unicode 字符具有多个表示形式。 例如,以下任何码位都可以表示字母“ắ”:

  • U+1EAF

  • U+0103 U+0301

  • U+0061 U+0306 U+0301

    单个字符的多个表示形式使搜索、排序、匹配和其他字符串操作复杂化。

    Unicode 标准定义了一个称为规范化的过程,该进程为 Unicode 字符的任何等效二进制表示形式返回一个二进制表示形式。 规范化可以使用多种算法(称为规范化形式),这些算法遵循不同的规则。 .NET 支持 Unicode 规范化形式 C、D、KC 和 KD。 当字符串已规范化为相同的规范化形式时,可以使用序号比较来比较它们。

    序号比较是每个字符串中相应 Char 对象的 Unicode 标量值的二进制比较。 类 String 包含许多可以执行序号比较的方法,其中包括:

  • 包含 StringComparison 参数的 Compare Equals 、、 StartsWith IndexOf EndsWith 、 和 LastIndexOf 方法的任何重载。 如果为此参数提供 或 StringComparison.Ordinal OrdinalIgnoreCase 值,则方法将执行序号比较。

  • 方法的 CompareOrdinal 重载。

  • 默认使用序号比较的方法,例如 Contains Replace Split

  • 在字符串实例的数组中 Char 搜索 Char 值或元素的方法。 此类方法包括 IndexOf(Char) Split(Char[])

    可以通过调用 String.IsNormalized() 方法来确定字符串是否规范化为规范化形式 C,也可以调用 String.IsNormalized(NormalizationForm) 方法来确定是否将字符串规范化为指定的规范化形式。 还可以调用 String.Normalize() 方法将字符串转换为规范化形式 C,也可以调用 String.Normalize(NormalizationForm) 方法将字符串转换为指定的规范化形式。 有关规范化和比较字符串的分步信息,请参阅 Normalize() Normalize(NormalizationForm) 方法。

    下面的简单示例演示了字符串规范化。 它在三个不同的字符串中以三种不同的方式定义字母“ố”,并使用相等的序号比较来确定每个字符串与其他两个字符串不同。 然后,它将每个字符串转换为支持的规范化形式,并再次对指定规范化形式中的每个字符串执行序号比较。 在每种情况下,第二个相等性测试都表明字符串相等。

    using namespace System; using namespace System::Globalization; using namespace System::IO; using namespace System::Text; public ref class Example private: StreamWriter^ sw; void TestForEquality(... array<String^>^ words) for (int ctr = 0; ctr <= words->Length - 2; ctr++) for (int ctr2 = ctr + 1; ctr2 <= words->Length - 1; ctr2++) sw->WriteLine("{0} ({1}) = {2} ({3}): {4}", words[ctr], ShowBytes(words[ctr]), words[ctr2], ShowBytes(words[ctr2]), words[ctr]->Equals(words[ctr2], StringComparison::Ordinal)); String^ ShowBytes(String^ str) String^ result = nullptr; for each (Char ch in str) result += String::Format("{0} ", Convert::ToUInt16(ch).ToString("X4")); return result->Trim(); array<String^>^ NormalizeStrings(NormalizationForm nf, ... array<String^>^ words) for (int ctr = 0; ctr < words->Length; ctr++) if (! words[ctr]->IsNormalized(nf)) words[ctr] = words[ctr]->Normalize(nf); return words; public: void Execute() sw = gcnew StreamWriter(".\\TestNorm1.txt"); // Define three versions of the same word. String^ s1 = L"sống"; // create word with U+1ED1 String^ s2 = L"s\x00F4\x0301ng"; String^ s3 = L"so\x0302\x0301ng"; TestForEquality(s1, s2, s3); sw->WriteLine(); // Normalize and compare strings using each normalization form. for each (String^ formName in Enum::GetNames(NormalizationForm::typeid)) sw->WriteLine("Normalization {0}:\n", formName); NormalizationForm nf = (NormalizationForm) Enum::Parse(NormalizationForm::typeid, formName); array<String^>^ sn = NormalizeStrings(nf, s1, s2, s3 ); TestForEquality(sn); sw->WriteLine("\n"); sw->Close(); void main() Example^ ex = gcnew Example(); ex->Execute(); // The example produces the following output: // The example displays the following output: // sống (0073 1ED1 006E 0067) = sống (0073 00F4 0301 006E 0067): False // sống (0073 1ED1 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False // sống (0073 00F4 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False // Normalization FormC: // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // Normalization FormD: // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // Normalization FormKC: // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // Normalization FormKD: // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True using System; using System.Globalization; using System.IO; using System.Text; public class Example private static StreamWriter sw; public static void Main() sw = new StreamWriter(@".\TestNorm1.txt"); // Define three versions of the same word. string s1 = "sống"; // create word with U+1ED1 string s2 = "s\u00F4\u0301ng"; string s3 = "so\u0302\u0301ng"; TestForEquality(s1, s2, s3); sw.WriteLine(); // Normalize and compare strings using each normalization form. foreach (string formName in Enum.GetNames(typeof(NormalizationForm))) sw.WriteLine("Normalization {0}:\n", formName); NormalizationForm nf = (NormalizationForm) Enum.Parse(typeof(NormalizationForm), formName); string[] sn = NormalizeStrings(nf, s1, s2, s3); TestForEquality(sn); sw.WriteLine("\n"); sw.Close(); private static void TestForEquality(params string[] words) for (int ctr = 0; ctr <= words.Length - 2; ctr++) for (int ctr2 = ctr + 1; ctr2 <= words.Length - 1; ctr2++) sw.WriteLine("{0} ({1}) = {2} ({3}): {4}", words[ctr], ShowBytes(words[ctr]), words[ctr2], ShowBytes(words[ctr2]), words[ctr].Equals(words[ctr2], StringComparison.Ordinal)); private static string ShowBytes(string str) string result = null; foreach (var ch in str) result += $"{(ushort)ch:X4} "; return result.Trim(); private static string[] NormalizeStrings(NormalizationForm nf, params string[] words) for (int ctr = 0; ctr < words.Length; ctr++) if (! words[ctr].IsNormalized(nf)) words[ctr] = words[ctr].Normalize(nf); return words; // The example displays the following output: // sống (0073 1ED1 006E 0067) = sống (0073 00F4 0301 006E 0067): False // sống (0073 1ED1 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False // sống (0073 00F4 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False // Normalization FormC: // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // Normalization FormD: // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // Normalization FormKC: // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // Normalization FormKD: // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True open System open System.IO open System.Text use sw = new StreamWriter(@".\TestNorm1.txt") let showBytes (str: string) = let mutable result = "" for ch in str do result <- result + $"{uint16 ch:X4} " result.Trim() let testForEquality (words: string[]) = for ctr = 0 to words.Length - 2 do for ctr2 = ctr + 1 to words.Length - 1 do sw.WriteLine("{0} ({1}) = {2} ({3}): {4}", words[ctr], showBytes(words[ctr]), words[ctr2], showBytes(words[ctr2]), words[ctr].Equals(words[ctr2], StringComparison.Ordinal)) let normalizeStrings nf (words: string[]) = for i = 0 to words.Length - 1 do if not (words[i].IsNormalized nf) then words[i] <- words[i].Normalize nf words // Define three versions of the same word. let s1 = "sống" // create word with U+1ED1 let s2 = "s\u00F4\u0301ng" let s3 = "so\u0302\u0301ng" testForEquality [| s1; s2; s3 |] sw.WriteLine() // Normalize and compare strings using each normalization form. for formName in Enum.GetNames typeof<NormalizationForm> do sw.WriteLine("Normalization {0}:\n", formName) let nf = Enum.Parse(typeof<NormalizationForm>, formName) :?> NormalizationForm let sn = normalizeStrings nf [| s1; s2; s3|] testForEquality sn sw.WriteLine "\n" // The example displays the following output: // sống (0073 1ED1 006E 0067) = sống (0073 00F4 0301 006E 0067): False // sống (0073 1ED1 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False // sống (0073 00F4 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False // Normalization FormC: // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // Normalization FormD: // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // Normalization FormKC: // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True // Normalization FormKD: // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True // sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True Imports System.Globalization Imports System.IO Imports System.Text Module Example Private sw As StreamWriter Public Sub Main() sw = New StreamWriter(".\TestNorm1.txt") ' Define three versions of the same word. Dim s1 As String = "sống" ' create word with U+1ED1 Dim s2 AS String = "s" + ChrW(&h00F4) + ChrW(&h0301) + "ng" Dim s3 As String = "so" + ChrW(&h0302) + ChrW(&h0301) + "ng" TestForEquality(s1, s2, s3) sw.WriteLine() ' Normalize and compare strings using each normalization form. For Each formName In [Enum].GetNames(GetType(NormalizationForm)) sw.WriteLine("Normalization {0}:", formName) Dim nf As NormalizationForm = CType([Enum].Parse(GetType(NormalizationForm), formName), NormalizationForm) Dim sn() As String = NormalizeStrings(nf, s1, s2, s3) TestForEquality(sn) sw.WriteLine(vbCrLf) sw.Close() End Sub Private Sub TestForEquality(ParamArray words As String()) For ctr As Integer = 0 To words.Length - 2 For ctr2 As Integer = ctr + 1 To words.Length - 1 sw.WriteLine("{0} ({1}) = {2} ({3}): {4}", words(ctr), ShowBytes(words(ctr)), words(ctr2), ShowBytes(words(ctr2)), words(ctr).Equals(words(ctr2), StringComparison.Ordinal)) End Sub Private Function ShowBytes(str As String) As String Dim result As String = Nothing For Each ch In str result += String.Format("{0} ", Convert.ToUInt16(ch).ToString("X4")) Return result.Trim() End Function Private Function NormalizeStrings(nf As NormalizationForm, ParamArray words() As String) As String() For ctr As Integer = 0 To words.Length - 1 If Not words(ctr).IsNormalized(nf) Then words(ctr) = words(ctr).Normalize(nf) End If Return words End Function End Module ' The example displays the following output: ' sống (0073 1ED1 006E 0067) = sống (0073 00F4 0301 006E 0067): False ' sống (0073 1ED1 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False ' sống (0073 00F4 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False ' Normalization FormC: ' sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True ' sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True ' sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True ' Normalization FormD: ' sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True ' sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True ' sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True ' Normalization FormKC: ' sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True ' sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True ' sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True ' Normalization FormKD: ' sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True ' sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True ' sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True

    有关规范化和规范化形式的详细信息,请参阅 System.Text.NormalizationForm unicode.org 网站上的 Unicode 标准附件 #15:Unicode 规范化形式 规范化常见问题解答

    按类别划分的字符串操作

    String 提供用于比较字符串、测试字符串是否相等、查找字符串中的字符或子字符串、修改字符串、从字符串中提取子字符串、合并字符串、设置值格式、复制字符串和规范化字符串的成员。

    比较字符串

    可使用以下 String 方法比较字符串以确定其在排序顺序中的相对位置:

  • Compare 返回一个整数,指示排序顺序中一个字符串与第二个字符串的关系。

  • CompareOrdinal 返回一个整数,该整数指示一个字符串与第二个字符串的关系,具体取决于其码位的比较。

  • CompareTo 返回一个整数,指示当前字符串实例与排序顺序中第二个字符串的关系。 方法 CompareTo(String) 为 类提供 IComparable IComparable<T> 实现 String

    测试字符串的相等性

    调用 Equals 方法以确定两个字符串是否相等。 实例 Equals(String, String, StringComparison) 和静态 Equals(String, StringComparison) 重载允许指定比较是区分区域性还是区分序号,以及是考虑还是忽略大小写。 大多数相等性测试都是序号的,确定对系统资源 ((如文件系统对象) )访问权限的相等性比较应始终为序号。

    查找字符串中的字符

    String 包括两种类型的搜索方法:

  • 返回值 Boolean 以指示字符串实例中是否存在特定子字符串的方法。 Contains 其中包括 、 EndsWith StartsWith 方法。

  • 指示子字符串在字符串实例中的起始位置的方法。 其中包括 、 IndexOf IndexOfAny LastIndexOf LastIndexOfAny 方法。

    如果要在字符串中搜索特定模式而不是特定子字符串,则应使用正则表达式。 有关详细信息,请参阅 .NET 正则表达式

    修改字符串

    String 包括以下方法,这些方法似乎用于修改字符串的值:

  • Insert 将字符串插入到当前 String 实例中。

  • PadLeft 在字符串的开头插入指定字符的一个或多个匹配项。

  • PadRight 在字符串的末尾插入指定字符的一个或多个匹配项。

  • Remove 从当前 String 实例中删除子字符串。

  • Replace 将子字符串替换为当前 String 实例中的另一个子字符串。

  • ToLower 并将 ToLowerInvariant 字符串中的所有字符转换为小写。

  • ToUpper ToUpperInvariant 将字符串中的所有字符转换为大写。

  • Trim 从字符串的开头和结尾删除字符的所有匹配项。

  • TrimEnd 从字符串末尾删除字符的所有匹配项。

  • TrimStart 从字符串开头删除字符的所有匹配项。

    所有字符串修改方法都返回一个新的 String 对象。 它们不会修改当前实例的值。

    从字符串中提取子字符串

    方法 String.Split 将单个字符串分隔为多个字符串。 方法的重载允许指定多个分隔符、限制方法提取的子字符串数、从子字符串中剪裁空格,以及指定是否 (空字符串在分隔符相邻) 包含在返回的字符串之间时发生。

    合并字符串

    以下 String 方法可用于字符串串联:

  • Concat 将一个或多个子字符串合并到单个字符串中。

  • Join 将一个或多个子字符串连接到单个元素中,并在每个子字符串之间添加分隔符。

    设置值的格式

    方法 String.Format 使用复合格式设置功能将字符串中的一个或多个占位符替换为某些对象或值的字符串表示形式。 方法 Format 通常用于执行以下操作:

  • 在字符串中嵌入数值的字符串表示形式。

  • 在字符串中嵌入日期和时间值的字符串表示形式。

  • 在字符串中嵌入枚举值的字符串表示形式。

  • 在字符串中嵌入支持 IFormattable 接口的某个对象的字符串表示形式。

  • 右对齐或向左对齐较大字符串内字段中的子字符串。

    有关格式设置操作和示例的详细信息,请参阅 Format 重载摘要。

    复制字符串

    可以调用以下 String 方法来创建字符串的副本:

  • Clone 返回对现有 String 对象的引用。

  • Copy 创建现有字符串的副本。

  • CopyTo 将字符串的一部分复制到字符数组。

    规范化字符串

    在 Unicode 中,单个字符可以有多个码位。 规范化将这些等效字符转换为相同的二进制表示形式。 方法 String.Normalize 执行规范化,方法 String.IsNormalized 确定是否对字符串进行规范化。

    有关详细信息和示例,请参阅本主题前面的 规范化 部分。

    ToImmutableDictionary<TSource,TKey,TValue>(IEnumerable<TSource>, Func<TSource,TKey>, Func<TSource,TValue>, IEqualityComparer<TKey>, IEqualityComparer<TValue>)

    枚举并转换序列,然后使用指定的键和值比较器生成其内容的不可变字典。

    GroupBy<TSource,TKey,TElement,TResult>(IEnumerable<TSource>, Func<TSource, TKey>, Func<TSource,TElement>, Func<TKey,IEnumerable<TElement>, TResult>, IEqualityComparer<TKey>)

    根据指定的键选择器函数对序列中的元素进行分组,并且从每个组及其键中创建结果值。 通过使用指定的比较器对键值进行比较,并且通过使用指定的函数对每个组的元素进行投影。

    GroupJoin<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,IEnumerable<TInner>, TResult>)

    基于键值等同性对两个序列的元素进行关联,并对结果进行分组。 使用默认的相等比较器对键进行比较。

    GroupJoin<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,IEnumerable<TInner>, TResult>, IEqualityComparer<TKey>)

    基于键值等同性对两个序列的元素进行关联,并对结果进行分组。 使用指定的 IEqualityComparer<T> 对键进行比较。

    Join<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,TInner,TResult>, IEqualityComparer<TKey>)

    基于匹配键对两个序列的元素进行关联。 使用指定的 IEqualityComparer<T> 对键进行比较。

  •