String
System.String类专门用于存储字符串,允许对字符串进行许多操作。此外,由于这种数据类型非常重要,C# 提供了它自己的关键字和相关的语法,以便使用这个类来轻松地处理字符串。使用运算符重载可以连接字符串:
123 | string messagel = ''Hello"; // returns "Hello" messagel = ”,There"; // returns "Hello, There"string message2 = messagel "!"; // returns "Hello, There! " |
---|
c#还允许使用类似于索引器的语法来提取指定的字符:
12 | string message = ''Hello”;char char4 = message[4]; // returns "0" |
---|
这个类可以完成许多常见的任务,如替换字符、删除空白和把字母变成大写形式等。
string 和 stringbuilder
String类是一个功能非常强大的类,它实现许多很有用的方法。但是,String类存在一个问题: 重复修改给定的字符串,效率会很低,它实际上是一个不可变的数据类型,这意味着一旦对字符串对象进行了 初始化,该字符串对象就不能改变了。
12 | string greetingText = "Hello from all the people at Wrox Press.greetingText = **We do hope you enjoy this book as much as we enjoyed writing it."; |
---|
在执行这段代码时,首先创建一个System.String类型的对象,并把它初始化为文本“Hello from all lhepeople at Wrox Press. ”,注意句号后面有一个空格。此时.NET运行库会为该字符串分配足够的内存来保存这个文本(41 个字符),再设置变量greetingText来表示这个字符串实例。
从语法上看,下一行代码是把更多的文本添加到字符串中。实际上并非如此,在此是创建一个新字符串实 例,给它分配足够的内存,以存储合并的文本(共104个字符)。把最初的文本“Hello from all the people at Wrox Press. ” 复制到这个新字符串中,再加上额外的文本 “We do hope you enjoy this book as much as we enjoyed writing it.”。然后更新存储在变量greetingText中的地址,使变量正确地指向新的字符串对象。现在没有引用旧的字符串 对象一不再有变量引用它,下一次垃圾收集器清理应用程序中所有未使用的对象时,就会删除它。
StringBuilder类则不同,每次操作都是对自身对象进行操作,而不是生成新的对象,其所占空间会随着内容的增加而扩充,这样,在做大量的修改操作时,不会因生成大量匿名对象而影响系统性能。
1 | var sb = new StringBuilder("Hello"); |
---|
String是不可变类,StringBuilder是可变类。
字符串格式
$前缀
12 | string si = "World"; string s2 = $"Hello, {s1}“; |
---|
在现实中,这只是语法糖。对于带$前缀的字符串,编译器创建String.Format方法的调用。所以前面的代码段解读为:
12 | string si = "World";string s2 = String.Format("Hello, {0}", si); |
---|
StringFormat
StringTormat方法的第一个参数接受一个格式字符串,其中的占位符从0开始编号,其后是放入字符串空白处的参数。新的字符串格式要方便得多,不需要编写那么多代码。
不仅可以使用变量来填写字符串的空白处,还可以使用返回一个值的任何方法:
1 | string s2 = $”Hello, {<!--{C}-->si. ToUpper() } '*; |
---|
这段代码可解读为如下类似的语句:
1 | string s2 = String.Format(nHello, {<!--{C}-->0}”,si.ToUpper ()); |
---|
字符串还可以有多个空白处,如下所示的代码:
12 | int x = 3, y = 4;string s3 = $"MThe result of {x} {y} is {x y}'"; |
---|
解读为:
1 | string s3 = String.Format("The result of {0} and {1} is {2 } ", x, y, x y); |
---|
转义花括号
如果希望在插值字符串中包括花括号,可以使用两个花括号转义它们:
12 | string s = "Hello";Console.WriteLine($"{{s}} displays the value of s: {s}"); |
---|
WriteLine方法被解读为如下实现代码:
1 | Console.WriteLine (String.Format (*'{<!--{C}-->s} displays the value of s: {<!--{C}-->0}", s)); |
---|
输出如下:
{s} displays the value of s : Hello
还可以转义花括号,从格式字符串中建立一个新的格式字符串。下面看看这个代码段:
123 | string formatstring = $" {s}, {{0 } } "; string s2 = "World";WriteLine(formatstring, s2); |
---|
有了字符串变量formatstring,编译器会把占位符0插入变量s,调用String.Format:
1 | string formatstring = String. Format ("{0}, {<!--{C}-->{0} } ", s); |
---|
这会生成格式字符串,其中变量s替换为值Hello,删除第二个格式最外层的花括号:
1 | string formatstring = "Hello, {0}"; |
---|
在WriteLine方法的最后一行,使用变量s2的值把World字符串插值到新的占位符0中:
1 | WriteLine("Hello, World"); |
---|
日期时间和数字的格式
除了给占位符使用字符串格式之外,还可以根据数据类型使用特定的格式。下面先从日期开始。在占位符 中,格式字符串跟在表达式的后面,用冒号隔开。下面所示的例子是用于DateTime类型的D和d格式:
123 | var day = new DateTime(2025, 2, 14); WriteLine ($"{day:D}");WriteLine ($"{day:d}”); |
---|
结果显示,用大写字母D表示长日期格式字符串,用小写字母d表示短日期字符串:
12 | Friday, February 14, 2025 2/14/2025 |
---|
应该提到的一个问题是,为DateTime构建自定义的格式字符串。自定义的日期和时间格式字符串可以结合 格式说明符,例如dd-MMM-yyyy:
1 | Console.WriteLine($"{day:dd-MMM-yyyy}"); |
---|
结果如下:
14-Feb-2025
这个自定义格式字符串利用dd把日期显示为两个数字(如果某个日期在10日之前,这就很重要,从这里可以看到d和dd之间的区别)、MMM(月份的缩写名称,注意它是大写,而mm表示分钟)和表示四位数年份的yyyy。
数字的格式字符串不区分大小写。下面看看n、e、x和c标准数字格式字符串:
12 | int i = 2477;Console.WriteLine($"{i:n} {i:e} {i:x} {i:c}H); |
---|
n格式字符串定义了一个数字格式,用组分隔符显示整数和小数。e表示使用指数表示法,x表示转换为十六进制,c显示货币:
2,477.00 2.477000e 003 9ad $2,477.00
对于数字的表示,还可以使用定制的格式字符串。#格式说明符是一个数字占位符,如果数字可用,就显示 数字;如果数字不可用,就不显示数字。0格式说明符是一个零占位符,显示相应的数字,如果数字不存在
正则表达式
正则表达式语言是一种专门用于字符串处理的语言。它包含两个功能:
- 一组用于标识特殊字符类型的转义代码。你可能很熟悉DOS命令中使用字符表示任意子字符串(例如, DOS命令DirRe会列出名称以Re开头的所有文件)。正则表达式使用与*类似的许多序列来表示“任 意一个字符”、“一个单词的中断”和个可选的字符”等。一个系统,在搜索操作中把子字符串和中1:司结果的各个部分组合起来。
- 识别(可以是标记或删除)字符串中所有重复的单词,例如,把“The computer books books”转换为“The computer books"
1 | const string text = "Professional C# 6 and .NET Core 1.0 provides complete coverage " "of the latest updates, features, and capabilities, giving you " "everything you need for C#. Get expert instruction on the latest " "changes to Visual Studio 2015, Windows Runtime, ADO.NET, ASP.NET, " "Windows Store Apps, Windows Workflow Foundation, and more, with " "clear explanations, no-nonsense pacing, and valuable expert insight. " "This incredibly useful guide serves as both tutorial and desk " "reference, providing a professional-level review of C# architecture " "and its application in a number of areas. You'll gain a solid " "background in managed code and .NET constructs within the context of " "the 2015 release, so you can get acclimated quickly and get back to work.";Console.WriteLine("Find1n");const string pattern = "ion"; MatchCollection matches = Regex.Matches(text, pattern, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);WriteMatches(text, matches);Console.WriteLine(); |
---|
在这段代码中,使用了 System.Text.RegularExpressions名称空间中Regex类的静态方法Matches()0这个方 法的参数是一些输入文本、一个模式和从RegexOptions枚举中提取的一组可选标志.
表描述了 RegexOptions枚举的一些成员。
正则匹配主要符号规则如下:
组
在默认情况下,把模式的一部分组合为一个组时,就要求正则表达式引擎按照该组来匹配,或按照整个模式来匹配。换言之,可以把组当成一个要匹配和返回的模式。如果要把字符 串分解为各个部分,这种模式就非常有效。
2 | public static void NamedGroups(){ Console.WriteLine("NamedGroupsn"); string line = "Hey, I've just found this amazing URI at http:// what was it --oh yes https://www.wrox.com or http://www.wrox.com:80"; string pattern = @"b(?<protocol>https?)(?:://)(?<address>[.w] )([s:](?<port>[d]{2,4})?)b"; Regex r = new Regex(pattern, RegexOptions.ExplicitCapture); MatchCollection mc = r.Matches(line); foreach (Match m in mc) { Console.WriteLine($"match: {m} at {m.Index}"); foreach (var groupName in r.GetGroupNames()) { Console.WriteLine($"match for {groupName}: {m.Groups[groupName].Value}"); } Console.WriteLine(); } Console.WriteLine();} |
---|