字符、字符串和文本的处理之String类型
.Net Framework中处理字符和字符串的主要有以下这么几个类:
(1)、System.Char类 一基础字符串处理类
(2)、System.String类 一处理不可变的字符串(一经创建,字符串便不能以任何方式修改)
(3)、System.Text.StringBuilder类 一更高效地构建字符串
(4)、System.Secureity.SecureString类 一对字符串进行保护操作,它可以保护密码和信用卡资料等敏感字符串.
一、String类型
1、简介
在任何应用程序里面,String类型都是用的最多的类型之一.一个String代表一个不可变的(immutable)的顺序字符串,String类型直接派生自Object,所以它是引用类型.所以String对象(它的字符串数组)总是存在于堆上,永远不会跑到线程栈.
C#将String视为基元类型,C#编译器允许在源代码中直接使用字面值字符串.编译器将这些字符串存放到模块的元数据中,并在运行时加载和引用它们.
C#不允许使用new操作符从字面值字符串构造String对象,代码如下:
相反,必须使用以下简化语法:
编译代码并检查IL(使用ILDasm.exe),会看到以下内容:
用于构造对象新实例,但上述代码中并没有出现newobj指令,有一个特殊的ldstr(即 load string)指令,它使用从元数据获得的字面值(literal)字符串构造String对象.这证明CLR实际是用一种特殊方式构造字面值String对象
如果使用不安全的(unsafe)代码,可以从一个Char*或Sbyte*参数构造一个String.这时要使用C#的new操作符,并调用由String类型提供的、能接受Char*或Sbyte*参数的某个构造器.这些构造器将创建String对象,根据由Char实例或有符号(signed)字节构成的一个数组来初始化字符串。其他构造器则不允许接受任何指针参数,用任何托管编程语言写的安全(可验证)代码都能调用它们.
2、关于特殊字符的处理:
C#提供了一些特殊的语法来帮助开发人员在源代码中输入字面值(literal)字符串,对于换行符、回车符和退格符这样的特殊字符,C#采用的是C/C++开发人员熟悉的转移机制,代码如下:
//包含回车符和换行符的字符串 string s="Hi\r\nthere.";
3、关于字符串连接的问题
string s="Hi"+" "+"there .";
在上述代码中,由于所有字符串都是字面值,所以C#编译器能在编译时连接它们,最终将一个字符串即(即"Hi there.")放到模块的元数据中.对非字面值字符串使用+操作符,连接则在运行时进行.运行时连接不要用+操作符,因为这样会在堆上创建多个字符串对象,而堆是需要垃圾回收的,对性能有影响.相反,应该使用StringBuilder类型.
4、字符串@转义符
C#提供了一种特殊的字符串声明方式.采取这种方式,印号之间的所有字符都会被视为字符串的一部分.这种特殊声明称为"逐字字符串",通常用于指定文件或目录的路径,或者与正则表达式配合使用。以下代码展示了如何使用和不适用逐字字符串字符(@)来申明同一个字符串,代码如下:
//指定应用程序路径,使用\解析'\' string file="C:\\Windows\\System32\\Notpad.exe"; //使用逐字字符串制定应用程序路径 string file=@"C:\Windows\System32\Notepad.exe";
两种写法生成完全一样的字符串,但后者的可读性更好.
5、字符串是不可变的
string对象最重要的一点就是不可变性.也就是所,字符串一经创建遍不能修改其中的任何字符.是字符串不可变有几方面的好处.
字符串不可变有几个方面的好处:
(1)、它允许在一个字符串上执行各种操作,而不实际地更改字符串,如下代码:
//创建一个字符串 string str = "zhengchao"; //然后将字符串转大写,ToUpperInvariant会创建一个临时字符串,垃圾回收器会在下次回收时回收 var result = str.ToUpperInvariant(); //输出:false,说明ToUpperInvariant返回了一个新的字符串 Console.WriteLine("str:'{0}' is Equals result? The answer is {1}",str, ReferenceEquals(str, result)); //字符串截取,ToUpperInvariant会创建一个临时字符串,垃圾回收器会在下次回收时回收 var result1 = result.Substring(2,3); //输出:false,说明Substring返回了一个新的字符串 Console.WriteLine("str is Equals result? The answer is {0}", ReferenceEquals(result, result1)); //EndsWith对目标字符进行检查不会创建临时字符串 var result2 = result.EndsWith("ENG");
代码不会长时间引用由ToUpperInvariant和Substring创建的两个临时字符串,垃圾回收器会在下次回收时回收它们的内存.如果执行大量字符串操作,会在堆上创建大量String对象,造成更频繁的垃圾回收,从而影响应用程序性能.so,要高效执行大量字符串操作,建议使用StringBuilder类.
字符串的不可变意味着在操纵或访问字符串时不会发生线程同步问题.此外,CLR可通过一个String对象共享多个完全一直的String内容.
这样能减少系统中的字符串数量一从而节省内存一这就是所谓的"字符串留用".
(2)、实例唯一性
在内存中复制同一个字符串的实例纯属浪费,因为字符串是"不可变"的.在内存中只保留字符串的一个实例将显著提升内存的利用率。需要引用字符串的所有变量只需指向单独的一个字符串对象.
代码如下:
string str1 = "xiaochao"; string str2 = "xiaochao"; Console.WriteLine(ReferenceEquals(str1, str2));//输出:True,说明str1和str2实例指向的是同一个对象
出于对性能的考虑,String类型与CLR紧密集成.具体地说,CLR知道String类型中定义的字段如何布局,会直接访问这些字段.但是为了获得这种性能和直接访问的好处,String只能是密封类.换言之,不能把它作为自己类型的基类.
如果允许String作为基类来定义自己的类型,就能添加自己的字段,而这会破坏CLR对于String类型的各种预设.此外还可能破坏CLR团队应为String对象"不可变"而做出的各种预设.
6、CLR关于语言文化的类型一CultureInfo类和字符串与线程的关联
7、C# 字符串操作基本过程(Equals、Compare、EndsWith等处理方法)
9、C# 高效率创建字符串类(StringBuilder).
11、C# 自定义类型通过实现IFormattable接口,来输出指定的格式和语言文化的字符串(例:DateTime)
12、C# String.Format的格式限定符与Format方法将多个对象格式化一个字符串原理