在应用程序中使用最频繁的类型是字符串。字符串简称串,是一种特殊的线 性表,其特殊性在于串中的数据元素是一个个的字符。字符串在计算机的许多方面应用很广。如在汇编和高级语言的编译程序中,源程序和目标程序都是字符串 数据。在事务处理程序中,顾客的信息如姓名、地址等及货物的名称、产地和规 格等,都被作为字符串来处理。另外,字符串还具有自身的一些特性。因此,把字符串作为一种数据结构来研究。
串的基本概念
串(String)由n(n ≥0)字符组成的有限序列。一般记为:S=”c1c2 …cn ”(n≥0) 其中,S是串名,双引号作为串的定界符,用双引号引起来的字符序列是串 值。ci(1≤i≤n )可以是字母、数字或其它字符,n为串的长度,当n=0时,称为空串(Empty String)。 串中任意个连续的字符组成的子序列称为该串的子串(Substring)。包含子串的串相应地称为主串。子串的第一个字符在主串中的位置叫子串的位置。
由于串中的字符都是连续存储的,而在C#中串具有恒定不变的特性,即字 符串一经创建,就不能将其变长、变短或者改变其中任何的字符。所以,这里不 讨论串的链式存储,也不用接口来表示串的操作。
串的基本操作的实现
1、求串长
求串的长度就是求串中字符的个数,可以通过求数组 data的长度来求串的长度。求串的长度的实现如下:
public int GetLength() { return data.Length; }
2、串比较
如果两个串的长度相等并且对应位置的字符相同,则串相等,返回 0;如果串s对应位置的字符大于该串的字符或者如果串s的长度大于该串,而在该串的 长度返回内二者对应位置的字符相同,则返回-1,该串小于串s;其余情况返回 1,该串大于串s。 串比较的算法实现如下:
public int Compare(StringDS s) { int len=((this.GetLength()<=s.GetLength())? this.GetLength():s.GetLength()); int i = 0; for (i = 0; i < len; ++i) { if (this[i] != s[i]) { break; } } if (i <= len) { if (this[i] < s[i]) { return -1; } else if (this[i] > s[i]) { return 1; } } else if(this.GetLength() s.GetLength()) { return 0; } else if (this.GetLength() < s.GetLength()) { return -1; } return 1; }
3、求子串
从主串的index位置起找长度为len的子串,若找到,返回该子串,否则,返回一个空串。 算法实现如下:
public StringDS SubString(int index, int len) { if ((index<0) || (index>this.GetLength()-1) || (len<0) || (len>this.GetLength()-index)) { Console.WriteLine("Position or Length is error!"); return null; } StringDS s = new StringDS(len); for (int i = 0; i < len; ++i) { s[i] = this[i + index-1]; } return s; }
4、串连接
将一个串和另外一个串连接成一个串,其结果返回一个新串,新串的长度是两个串的长度之和,新串的前部分是原串,长度为该串的长度,新串的后部分是串s,长度为串s的长度。 串连接的算法实现如下:
public StringDS Concat(StringDS s) { StringDS s1 = new StringDS(this.GetLength() + s.GetLength()); for(int i = 0; i < this.GetLength(); ++i) { s1.data[i] = this[i]; } for(int j = 0; j < s.GetLength(); ++j) { s1.data[this.GetLength() + j] = s[j]; } return s1; }
5、串插入
串插入是在一个串的位置index处插入一个串s。如果位置符合条件,则该操作返回一个新串,新串的长度是该串的长度与串s的长度之和,新串的第1部分是 该串的开始字符到第index之间的字符,第2部分是串s,第3部分是该串从index 位置字符到该串的结束位置处的字符。如果位置不符合条件,则返回一个空串。 串插入的算法如下:
public StringDS Insert(int index, StringDS s) { int len = s.GetLength(); int len2 = len + this.GetLength(); StringDS s1 = new StringDS(len2); if (index < 0 || index > this.GetLength() - 1) { Console.WriteLine("Position is error!"); return null; } for (int i = 0; i < index; ++i) { s1[i] = this[i]; } for(int i = index; i < index + len ; ++i) { s1[i] = s[i - index]; } for (int i = index + len; i < len2; ++i) { s1[i] = this[i - len]; } return s1; }
6、串删除
串删除是从把串的第index位置起连续的len个字符的子串从主串中删除掉。 如果位置和长度符合条件,则该操作返回一个新串,新串的长度是原串的长度减 去len,新串的前部分是原串的开始到第index个位置之间的字符,后部分是原串 从第index+len位置到原串结束的字符。如果位置和长度不符合条件,则返回一 个空串。 串删除的算法实现如下:
public StringDS Delete(int index, int len) { if ((index<0) || (index>this.GetLength()-1) || (len<0) || (len>this.GetLength()-index)) { Console.WriteLine("Position or Length is error!"); return null; } StringDS s = new StringDS(this.GetLength() - len); for (int i = 0; i < index; ++i) { s[i] = this[i]; } for (int i = index + len; i < this.GetLength(); ++i) { s[i] = this[i]; } return s; }
7、串定位
查找子串s在主串中首次出现的位置。如果找到,返回子串s在主串中首次出现的位置,否则,返回-1。 串定位的算法实现如下:
public int Index(StringDS s) { if (this.GetLength() < s.GetLength()) { Console.WriteLine("There is not string s!"); return -1; } int i = 0; int len = this.GetLength() - s.GetLength(); while (i < len) { if (Compare(s) 0) { break; } } if (i <= len) { return i; } return -1; }
在C#中,一个String 表示一个恒定不变的字符序列集合。String 类型是封闭类型,所以,它不能被其它类继承,而它直接继承自 object。因此,String 是引用类型,不是值类型,在托管堆上而不是在线程的堆栈上分配空间。String 类型还继承了 IComparable 、ICloneable 、IConvertible 、IComparable<string> 、IEnumerable<char>、IEnumerable 和IEquatable<string>等接口。String 的恒定性指的是一个串一旦被创建,就不能将其变长、变短或者改变其中任何的字符。所以,当我们对一个串进行操作时,不能改变字符串,C#也 提供了 StringBuilder 类型来支持高效地动态创建字符串。
在C#中,创建串不能用new 操作符,而是使用一种称为字符串驻留的机制。这是因为 C#语言将 String 看作是基元类型。基元类型是被编译器直接支持的类型,可以在源代码中用文本常量(Literal)来直接表达字符串。当C#编译器对源代码进行编译时,将文本常量字符串存放在托管模块的元数据中。而当 CLR 初始 化时,CLR创建一个空的散列表,其中的键是字符串,值为指向托管堆中字符 串对象的引用。散列表就是哈希表。当 JIT 编译器编译方法时,它会在散列表中查找每一个文本常量字符串。如果找不到,就会在托管堆中构造一个新的 String 对象(指向字符串),然后将该字符串和指 向该字符串对象的引用添加到散列表中;如果找到了,不会执行任何操作。