1.字符串
字符串是值为文本的 String 类型对象。 文本在内部存储为 Char 对象的依序只读集合。 在 C# 字符串末尾没有 null 终止字符;因此,一个 C# 字符串可以包含任何数量的嵌入的 null 字符 (' ')。 字符串的 Length 属性表示其包含的 Char 对象数量,而非 Unicode 字符数。
在 C# 中,string 关键字是 String 的别名。 因此,String 和 string 是等效的, String 类提供了安全创建、操作和比较字符串的多种方法。
2.声明和初始化字符串
可以使用各种方法声明和初始化字符串,如以下示例中所示:
string message1;
string message2 = null;
string message3 = System.String.Empty;
string oldPath = "c:\Program Files\Microsoft Visual Studio 8.0";
string newPath = @"c:Program FilesMicrosoft Visual Studio 9.0";
System.String greeting = "Hello World!";
var temp = "I'm still a strongly-typed System.String!";
const string message4 = "You can't get rid of me!";
char[] letters = { 'A', 'B', 'C' };
string alphabet = new string(letters);
请注意,不要使用 new 运算符创建字符串对象,除非使用字符数组初始化字符串。
使用 Empty 常量值初始化字符串,以新建字符串长度为零的 String 对象。 长度为零的字符串文本表示法是“”。 通过使用 Empty 值(而不是 null)初始化字符串,可以减少 NullReferenceException 发生的可能性。 尝试访问字符串前,先使用静态 IsNullOrEmpty(String) 方法验证字符串的值。
3.字符串对象的不可变性
字符串对象是“不可变的”:它们在创建后无法更改。 在下面的示例中,当 s1 和 s2 的内容被串联在一起以形成单个字符串时,两个原始字符串没有被修改。 += 运算符创建一个新的字符串,其中包含组合的内容。 这个新对象被分配给变量 s1,而分配给 s1 的原始对象被释放,以供垃圾回收,因为没有任何其他变量包含对它的引用。
string s1 = "A string is more ";
string s2 = "than the sum of its chars.";
s1 += s2;
System.Console.WriteLine(s1);
// Output: A string is more than the sum of its chars.
由于字符串“modification”实际上是一个新创建的字符串,因此,必须在创建对字符串的引用时使用警告。 如果创建了字符串的引用,然后“修改”了原始字符串,则该引用将继续指向原始对象,而非指向修改字符串时所创建的新对象。 以下代码阐释了此行为:
string s1 = "Hello ";
string s2 = s1;
s1 += "World";
System.Console.WriteLine(s2);
//Output: Hello
4.常规和逐字字符串文本
在必须嵌入 C# 提供的转义字符时,使用常规字符串文本,如以下示例所示:
string columns = "Column 1 Column 2 Column 3";
//Output: Column 1 Column 2 Column 3
string rows = "Row 1
Row 2
Row 3";
/* Output:
Row 1
Row 2
Row 3
*/
string title = ""The u00C6olean Harp", by Samuel Taylor Coleridge";
//Output: "The Æolean Harp", by Samuel Taylor Coleridge
当字符串文本包含反斜杠字符(例如在文件路径中)时,出于便捷性和更强的可读性的考虑,使用逐字字符串。 由于逐字字符串将新的行字符作为字符串文本的一部分保留,因此可将其用于初始化多行字符串。 使用双引号在逐字字符串内部嵌入引号。
下面的示例演示逐字字符串的一些常见用法:
string filePath = @"C:UsersscoleridgeDocuments";
//Output: C:UsersscoleridgeDocuments
string text = @"My pensive SARA ! thy soft cheek reclined
Thus on mine arm, most soothing sweet it is
To sit beside our Cot,...";
/* Output:
My pensive SARA ! thy soft cheek reclined
Thus on mine arm, most soothing sweet it is
To sit beside our Cot,...
*/
string quote = @"Her name was ""Sara.""";
//Output: Her name was "Sara."
5.格式字符串
格式字符串是可以在运行时以动态方式确定其内容的字符串。 使用静态 Format 方法,并在大括号中嵌入将在运行时被其他值替换的占位符,从而创建格式字符串。 下面的示例使用格式字符串来输出每个循环迭代的结果:
class FormatString
{
static void Main()
{
// Get user input.
System.Console.WriteLine("Enter a number");
string input = System.Console.ReadLine();
// Convert the input string to an int.
int j;
System.Int32.TryParse(input, out j);
// Write a different string each iteration.
string s;
for (int i = 0; i < 10; i++)
{
// A simple format string with no alignment formatting.
s = System.String.Format("{0} times {1} = {2}", i, j, (i * j));
System.Console.WriteLine(s);
}
//Keep the console window open in debug mode.
System.Console.ReadKey();
}
}
WriteLine 方法的一个重载将格式字符串用作参数。因此,可以仅嵌入格式字符串文本,而无需显式调用该方法。
6.子字符串
子字符串是包含在字符串中的任何字符序列。 使用 Substring 方法可以通过原始字符串的一部分新建字符串。 可以使用 IndexOf 方法搜索一次或多次出现的子字符串。 使用 Replace 方法可以将出现的所有指定子字符串替换为新字符串。 与 Substring 方法一样,Replace 实际返回的是新字符串,且不修改原始字符串。
string s3 = "Visual C# Express";
System.Console.WriteLine(s3.Substring(7, 2));
// Output: "C#"
System.Console.WriteLine(s3.Replace("C#", "Basic"));
// Output: "Visual Basic Express"
// Index values are zero-based
int index = s3.IndexOf("C");
// index = 7
7.访问单个字符
可以使用包含索引值的数组表示法来获取对单个字符的只读访问权限,如下面的示例中所示:
string s5 = "Printing backwards";
for (int i = 0; i < s5.Length; i++)
{
System.Console.Write(s5[s5.Length - i - 1]);
}
// Output: "sdrawkcab gnitnirP"
如果 String 方法不提供修改字符串中的各个字符所需的功能,可以使用 StringBuilder 对象“就地”修改各个字符,再新建字符串来使用 StringBuilder 方法存储结果。 在下面的示例中,假定必须以特定方式修改原始字符串,然后存储结果以供未来使用:
string question = "hOW DOES mICROSOFT wORD DEAL WITH THE cAPS lOCK KEY?";
System.Text.StringBuilder sb = new System.Text.StringBuilder(question);
for (int j = 0; j < sb.Length; j++)
{
if (System.Char.IsLower(sb[j]) == true)
sb[j] = System.Char.ToUpper(sb[j]);
else if (System.Char.IsUpper(sb[j]) == true)
sb[j] = System.Char.ToLower(sb[j]);
}
// Store the new string.
string corrected = sb.ToString();
System.Console.WriteLine(corrected);
// Output: How does Microsoft Word deal with the Caps Lock key?
8.Null 字符串和空字符串
空字符串是包含零个字符的 System.String 对象实例。 空字符串常用在各种编程方案中,表示空文本字段。 可以对空字符串调用方法,因为它们是有效的 System.String 对象。
对空字符串进行了初始化,如下所示:
string s = String.Empty;
相比较而言,null 字符串并不指 System.String 对象实例,只要尝试对 null 字符串调用方法,都会引发 NullReferenceException。
但是,可以在串联和与其他字符串的比较操作中使用 null 字符串。 以下示例说明了对 null 字符串的引用会引发和不会引发意外的某些情况:
static void Main()
{
string str = "hello";
string nullStr = null;
string emptyStr = String.Empty;
string tempStr = str + nullStr;
// Output of the following line: hello
Console.WriteLine(tempStr);
bool b = (emptyStr == nullStr);
// Output of the following line: False
Console.WriteLine(b);
// The following line creates a new empty string.
string newStr = emptyStr + nullStr;
// Null strings and empty strings behave differently. The following
// two lines display 0.
Console.WriteLine(emptyStr.Length);
Console.WriteLine(newStr.Length);
// The following line raises a NullReferenceException.
//Console.WriteLine(nullStr.Length);
// The null character can be displayed and counted, like other chars.
string s1 = "x0" + "abc";
string s2 = "abc" + "x0";
// Output of the following line: * abc*
Console.WriteLine("*" + s1 + "*");
// Output of the following line: *abc *
Console.WriteLine("*" + s2 + "*");
// Output of the following line: 4
Console.WriteLine(s2.Length);
}
9.使用 StringBuilder 快速创建字符串
.NET 中的字符串操作进行了高度的优化,在大多数情况下不会显著影响性能。 但是,在某些情况下(例如,执行数百次或数千次的紧密循环),字符串操作可能影响性能。 StringBuilder 类创建字符串缓冲区,用于在程序执行多个字符串操控时提升性能。 使用 StringBuilder 字符串,还可以重新分配各个字符,而内置字符串数据类型则不支持这样做。 例如,此代码更改字符串的内容,而无需创建新的字符串:
System.Text.StringBuilder sb = new System.Text.StringBuilder("Rat: the ideal pet");
sb[0] = 'C';
System.Console.WriteLine(sb.ToString());
System.Console.ReadLine();
//Outputs Cat: the ideal pet
在以下示例中,StringBuilder 对象用于通过一组数字类型创建字符串:
class TestStringBuilder
{
static void Main()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
// Create a string composed of numbers 0 - 9
for (int i = 0; i < 10; i++)
{
sb.Append(i.ToString());
}
System.Console.WriteLine(sb); // displays 0123456789
// Copy one character of the string (not possible with a System.String)
sb[0] = sb[9];
System.Console.WriteLine(sb); // displays 9123456789
}
}
10.搜索字符串
10.1 包含检测
String.Contains、String.StartsWith 和 String.EndsWith 方法搜索字符串中的特定文本。 下面的示例显示了每一个方法以及使用不区分大小写的搜索的差异:
string factMessage = "Extension methods have all the capabilities of regular static methods.";
// Write the string and include the quotation marks.
Console.WriteLine($""{factMessage}"");
// Simple comparisons are always case sensitive!
bool containsSearchResult = factMessage.Contains("extension");
Console.WriteLine($"Starts with "extension"? {containsSearchResult}");
// For user input and strings that will be displayed to the end user,
// use the StringComparison parameter on methods that have it to specify how to match strings.
bool ignoreCaseSearchResult = factMessage.StartsWith("extension", System.StringComparison.CurrentCultureIgnoreCase);
Console.WriteLine($"Starts with "extension"? {ignoreCaseSearchResult} (ignoring case)");
bool endsWithSearchResult = factMessage.EndsWith(".", System.StringComparison.CurrentCultureIgnoreCase);
Console.WriteLine($"Ends with '.'? {endsWithSearchResult}");
10.2 位置查询
IndexOf 和 LastIndexOf 方法也搜索字符串中的文本。 这些方法返回查找到的文本的位置。 如果未找到文本,则返回 -1。 下面的示例显示“methods”第一次出现和最后一次出现的搜索结果,并显示了它们之间的文本。
string factMessage = "Extension methods have all the capabilities of regular static methods.";
// Write the string and include the quotation marks.
Console.WriteLine($""{factMessage}"");
// This search returns the substring between two strings, so
// the first index is moved to the character just after the first string.
int first = factMessage.IndexOf("methods") + "methods".Length;
int last = factMessage.LastIndexOf("methods");
string str2 = factMessage.Substring(first, last - first);
Console.WriteLine($"Substring between "methods" and "methods": '{str2}'");
11.修改字符串
11.1 替换文本
下面的代码通过将现有文本替换为替代文本来创建新的字符串。
string source = "The mountains are behind the clouds today.";
// Replace one substring with another with String.Replace.
// Only exact matches are supported.
var replacement = source.Replace("mountains", "peaks");
Console.WriteLine($"The source string is <{source}>");
Console.WriteLine($"The updated string is <{replacement}>");
上述代码演示了字符串的不可变属性。 在上述示例中可以看到,原始字符串 source 并未被修改。 String.Replace 方法创建的是包含修改的新 string。
Replace 可替换字符串或单个字符。 在这两种情况下,搜索文本的每个匹配项均被替换。 下面的示例将所有的“ ”替换为“_”:
string source = "The mountains are behind the clouds today.";
// Replace all occurrences of one char with another.
var replacement = source.Replace(' ', '_');
Console.WriteLine(source);
Console.WriteLine(replacement);
源字符串并未发生更改,而是通过替换操作返回了一个新的字符串。
11.2 去除空格
可使用 String.Trim、String.TrimStart 和 String.TrimEnd 方法删除任何前导空格或尾随空格。 下面的代码就是删除两种空格的示例。 源字符串不会发生变化;这些方法返回带修改内容的新字符串。
// Remove trailing and leading white space.
string source = " I'm wider than I need to be. ";
// Store the results in a new string variable.
var trimmedResult = source.Trim();
var trimLeading = source.TrimStart();
var trimTrailing = source.TrimEnd();
Console.WriteLine($"<{source}>");
Console.WriteLine($"<{trimmedResult}>");
Console.WriteLine($"<{trimLeading}>");
Console.WriteLine($"<{trimTrailing}>");
11.3 删除文本
可使用 String.Remove 方法删除字符串中的文本。 此方法移删除特定索引处开始的某些字符。 下面的示例演示如何使用 String.IndexOf(后接 Remove)方法,删除字符串中的文本:
string source = "Many mountains are behind many clouds today.";
// Remove a substring from the middle of the string.
string toRemove = "many ";
string result = string.Empty;
int i = source.IndexOf(toRemove);
if (i >= 0)
{
result= source.Remove(i, toRemove.Length);
}
Console.WriteLine(source);
Console.WriteLine(result);
11.4 修改单个字符
可从字符串生成字符数组,修改数组的内容,然后从数组的已修改内容创建新的字符串。
下面的示例演示如何替换字符串中的一组字符。 首先,它使用 ToCharArray() 方法来创建字符数组。 它使用 IndexOf 方法来查找单词“fox”的起始索引。 接下来的三个字符将替换为其他单词。 最终,从更新的字符串数组中构造了新的字符串。
string phrase = "The quick brown fox jumps over the fence";
Console.WriteLine(phrase);
char[] phraseAsChars = phrase.ToCharArray();
int animalIndex = phrase.IndexOf("fox");
if (animalIndex != -1)
{
phraseAsChars[animalIndex++] = 'c';
phraseAsChars[animalIndex++] = 'a';
phraseAsChars[animalIndex] = 't';
}
string updatedPhrase = new string(phraseAsChars);
Console.WriteLine(updatedPhrase);
12.正则表达式
引入命名空间System.Text.RegularExpressions。
12.1 字符串替换
string line = "name = kyle, age = 23";
Regex reg = new Regex("age = (.+)");
string modified = reg.Replace(line, "age = 18");
12.2 字符串查找
string line = "name = kyle, age = 23";
Regex reg = new Regex("age = (.+)");
Match match = reg.Match(line);
string value1 = match.Groups[0].Value;
string value2 = match.Groups[1].Value;
Console.WriteLine(value1);
Console.WriteLine(value2);
output:
age = 23
23