.netframerwork中的字符串处理

  我们不管学习任何语言,字符串可以说是用得最多的对象之一。字符串在很多人看是很简单的,但是就算是简单我这里也系统的概括一下。

  这里我要介绍两点

  1,怎么样使用字符串

  2,字符串的格式化

  首先我们看看怎么使用字符串

  字符串的使用相信大家都很清楚,定义,赋值都是很简单的。

string oString = "EdrickLiu";

  但是这里我们要清楚这么一行代码的实际含义。首先string实际上是System.String的等量声明,这里我们实际上是声明了一个System.String类型的引用。oString引用指向托管堆上的一个String实例。所以string是引用类型。这里的一行代码实际上是等价于

String s = new String(new char[] { 'E','d','r','i','c','k','L','i','u' });

  这也说明了为什么我们可以把我们的字符串分解为char类型的数组。其实C#有很多简便的声明变量的方法,比如数组。那么既然字符串是引用类型,那么我们相加两个字符串为什么可以得到实际的字符串相加呢。因为string类重载了+和==运算符,运算符的重载请看我关于运算符重载的文章。这里就不介绍。这里只介绍如果相加两个字符串,背后会发生什么。

            string sayHello = "Hello";
            sayHello += ":Edrick";
            Console.WriteLine(sayHello);

  这是很简单的一两行代码,这两行代码背后的原理是,首先,我们会以文本"Hello"实例化一个新的string对象。sayHello指向这个对象。接着我们会在内存中创建另外一片区域来存储":Edrick"。然后再创建一个全新的内存区域,来保存两个连接的字符串。最后一步就是更新sayHello的引用,时它指向我们最后保存连接结果的那个对象。所以这里一共创建了3个对象,所以这样更新字符串的内容实际上是开销是非常大的。如果要频繁的使用string来更新文本,性能会有比较大的损失,当然我们有办法解决,后面会介绍另外一个能表示文本的对象

  string的原理我们搞清楚了,那么我们现在看看它的一般的使用技巧。

 static void Main(string[] args)
        {
            string testString = "A poll released Friday by the Civility Initiativeat The Johns Hopkins University";
            string testTrim=testString.Trim();//取出所有空白
            Console.WriteLine("Trim:{0}",testTrim);
            string[] chars = testString.Split(' ');//按照指定的元素分割string
            for (int i=0;i<chars.Length;i++)
            { 
             Console.WriteLine("Spilt:{0}",chars[i].ToString());
            }
            int iCompare = testString.CompareTo("poll");
            Console.WriteLine("CompareTo:{0}", iCompare);
            string stringConcat= string.Concat("oldString","newString");
            Console.WriteLine("Concat:{0}",stringConcat);
            char[] otherChars = new char[7];
            testString.CopyTo(0, otherChars, 0, 6);
            for (int i = 0; i < otherChars.Length; i++)
            {
                Console.WriteLine("CopyTo:{0}",otherChars[i].ToString());
              
            }
            string stringFormat=string.Format("Name:{0}","EdrickLiu");
            Console.WriteLine("StringFormat:{0}",stringFormat);
            int stringIndexOf = testString.IndexOf("poll");
            Console.WriteLine("stringIndexOf:{0}", stringIndexOf);
            string stringInsert = testString.Insert(testString.Length - 1, "this is insert value");
            Console.WriteLine("stringInsert:{0}",stringInsert);
            int stringLastIndexOf = testString.LastIndexOf("c");
            Console.WriteLine("stringLastIndexOf:{0}", stringLastIndexOf);
            string stringPadLeft=testString.PadLeft(testString.Length + 10, '*');
            Console.WriteLine("stringPadLeft:{0}", stringPadLeft);
            string stringPadRight = testString.PadRight(testString.Length + 10, '*');
            Console.WriteLine("stringPadLeft:{0}", stringPadLeft);
            string stringReplace = testString.Replace(' ','_');
            Console.WriteLine("stringReplace:{0}",stringReplace);
            string stringSubstring = testString.Substring(0, 10);
            Console.WriteLine("stringSubstring:{0}",stringSubstring);
            string stringToLower = testString.ToLower();
            Console.WriteLine("stringToLower:{0}",stringToLower);
            string stringToUpper = testString.ToUpper();
            Console.WriteLine("stringToUpper:{0}",stringToUpper);
            Console.Read();
        }

  以上是一些常用的用法,当然string还有很多有用的用法,特别是现在又许多扩展方法。我们刚刚提到,如果用string做大量的文本变换,是非常消耗性能的,如果要大量的操作文本,我们还有另外一个类StringBuilder

 

  StringBuilder

  stringBuilder也能表示文本,StringBuilder跟string比优点是如果进行频繁的文本更换,则效率比string高,缺点是没有string支持的属性方法多。那么为什么StringBuilder的执行效率要比string快呢?理论上来说,StringBuilder对象维护一个缓冲区,当我们实例化一个StringBuilder的时候就会默认的划分一块缓冲区,然后把我们的数据写入缓冲区。如果我们串连字符串的长度操作了默认的缓冲区的大小或我们分配的缓冲区的大小,就会新建另外一个缓冲区,然后把原始缓冲区内的数据和新数据一起复制到新的缓冲区内。如果没有超过大小,会直接把新数据加入到缓冲区内。这就跟string的工作方式不一样。string的工作方式上面介绍了。所以我们在使用StringBuilder的时候,最好给塔分配尽可能大的空间。默认的容量是16,默认最大容量为Int.Max.所以很多人问String跟StringBuilder的区别。其实不是一句简单string不是不可变,而StringBuilder是可变。答案还应该尽可能的完善

  string类型在我们初始化后就不能改变它的值,任何对string的操作都是生成的新的string对象,然后操作新对象,最后更改引用。而StringBuilder对象则是利用一块默认的或者我们分配的缓冲区来存储我们的字符串,当我们操作字符串的时候,如果在不超过容量的情况下会直接在缓冲区内操作,如果超过容量,则需要新建缓冲区然后再操作。所以,string操作的是对象,而StringBuilder操作的是缓冲区。这是我个人认为的String跟StringBuilder的区别。而不是简单的一个可变,一个不可变。而使用他们的场景是,string作为显示字符串,使用string的大量属性。或者简单的连接字符串。而StringBuilder是要对字符串进行大量的操作的时候,比如,替换。

 而StringBuilder的使用也是很简单的

            StringBuilder build = new StringBuilder(10000);
            build.Append("This");
            build.Append(" is");
            build.Append(" a");
            build.Append(" StringBuilder");
            Console.WriteLine(build.ToString());

  而它的属性和方法也不多。如果要替换字符串中的某个字符,可以使用Replace,要在指定的索引插入字符串可以使用insert,要删除指定索引的字符串使用Remove。最后使用ToString转换为String。它有两个属性,一个是Length,指的是字符串的长度,还有一个capacity。值的是StringBuilder的容量,如果没有指定容量,在我们做串联操作的时候,如果串联的字符串大于容量,则容量会成倍的增长。

  使用StringBuilder的时候,最好是给塔分配足够大的空间。

  

  格式化字符串

  格式化字符串是以特定的格式显示字符串,就是以用户希望的方式显示字符串或变量。使程序更加友好和容易阅读。先看一个简单的示例

            int i = 45;
            Console.WriteLine("You must pay {0:C}",i);

  这里我们会以美元符号显示,因为在字符串的解析遇到C的时候,i知道该如何处理这个字符。并且以定义的形式显示。其实这里就是等于

            int i = 45;
            Console.WriteLine("You must pay Y"+i);

  所谓的格式化字符串其实就是处理格式符,格式符就是一系列定义了的显示字符串的规则。那么我们先来解析一下为什么编译器能识别,最后我们编写一个自己的格式化符。首先,要使对象或变量能够格式化显示,就必须继承IFormattable接口。那么这里Console.WriteLine()实际上是Console.WriteLine(string.Format())。所以现在剩下的就是以定义的显示规则来替换格式符。现在我们来看看构建过程。构建过程需要StringBuilder类。

            int i = 45;
            StringBuilder builder = new StringBuilder(1000);
            builder.Append("You must pay");
            builder.AppendFormat("{0:C}", i);
            builder.Append(",so please give me");

  这就描述了格式化字符串的过程。在这个过程中添加两个常规的字符串不需要解释。关键是AppendFormat。下面我们来看看这个方法。这个方法接受两个参数,一个是格式化符,另外一个就是变量。首先会检查,这个变量有没有实现IFormattable接口。如果实现了这个接口就调用我们重载了格式符的tostring方法,然后构造字符串,加入到StringBuilder,所以这里就构造出Y34,然后加入到StringBuilder,如果没有格式符,就会按照原始的tostring输出对象。然后完成了整个构造过程。构造过程就是这样。现在我们就来自定义格式符。

  我们要完成的就是XXXX年XX月XX日来显示我们的一个类。首先定义类MyTime

 public class MyTime:IFormattable
    {
        private string _year;

        public string Year
        {
            get { return _year; }
            set { _year = value; }
        }

        private string _month;

        public string Month
        {
            get { return _month; }
            set { _month = value; }
        }

        private string _data;

        public string Data
        {
            get { return _data; }
            set { _data = value; }
        }

        public MyTime() { }

        public MyTime(string year, string month, string data)
        {
            this._year = year;
            this._month = month;
            this._data = data;
        }

        public override string ToString()
        {
            return Year + "/" + Month + "/" + Data;
        }

        public string ToString(string format, IFormatProvider formatProvider)
        {
            if (format == null)
            {
                return ToString();
            }
            else
            {
                string formatUpper = format.ToUpper();

                switch(formatUpper)
                {
                    case "TIME":
                        return this.Year + "年"+this.Month+"月"+this.Data+"日";
                    default:
                        return ToString();
                    
                }
            }
            
        }
    }

这里就是两个ToString,一个是没有格式化符号的要显示的字符串,另外一个是有格式化符显示的字符串。我们再看看调用代码

            MyTime time = new MyTime("2011","11","14");

            Console.WriteLine("Time is {0:TIME}", time);
            Console.WriteLine("Time out TIME is {0}",time);

这里我们就可以看到显示的结果,其实格式化字符串就是按照我们定义的方式显示特定的字符串。所以,如果我们这里要重写:C或者我们的程序的业务有自己特定的显示逻辑,也可以自定义。最后贴一张部分的系统定义的格式符。

  

  

posted @ 2011-11-14 07:06  刘中栋  阅读(424)  评论(0)    收藏  举报