本章内容:

    字符串基本必备知识的概述

    创建字符串

    格式化表达式(字符串)

概述:

    在C#的开发中我们用到最多的就是字符串了,而我们最容易忽视的也是字符串,所以我们就特意聊聊这个字符串。

    首先,在C#中string关键字的映射实际上指向.NET基类System.String。System.String是一个功能非常强大且用途非常广泛的基类。但它不是.NET库中唯一与字符串相关的类。我们首先学习一下System.String的特性,再介绍如何使用其他的.NET库类来处理字符串,特别是System.Text和System.Text.RegularExpressions名称空间中的类。

    我们主要研究的内容是:

    创建字符串:如果多次修改一个字符串,例如:在显示字符串或将其传递给其他方法或应用程序前,创建一个较长的字符串,String类就会变的效率低下。对于这种情况,应使用另一个类System.Text.StringBuilder(它是专门为了这种情况设计的)。

    格式化字符串:格式化表达式使用两个有效的接口IFormatProvider和IFormattable来处理。在自己的类上实现这两个接口,实际上就可以定义自己的格式化字符串,这样,Console.WriteLine()以及类似的类就可以以指定的方式显示类的值。

首先了解下System.String类:

    System.String是一个类,专门用来存储字符串。允许对字符串进行许多操作。由于这种数据类型非常重要。C#提供了它自己的关键字和相关的语法来处理字符串。

    例如:

        使用运算符重载可以连接字符串:

        string message1=“Hello”;//此时message1是Hello

        message+=”,There”;//此时message1是Hello,There

        string message2=message1 +”!”;此时message2是Hello,There!

        使用类似于索引器的语法来提取指定的字符:

        char char4=message[4];//此时char4返回的是字符“o”

    这个类为我们方便完成任务而提供的方法和相关的属性在MSDN上有最新最完整的描述哟:

       

创建字符串

    我们应该知道的是String类是一个功能非常强大的类,它实现许多很有用的方法。但是,String类存在一个问题:重复修改给定的字符串,效率会很低,它实际上是一个不可变的数据类型,一旦对字符串对象进行初始化,该字符串对象就不能改变了。表面上修改字符串内容的方法和运算符实际上创建一个新字符串,根据需要,可以把旧字符串的内容复制到新字符串中。

    string qreetingText=”Hello from all the guys at Wrox Press.  ” ;

    qreetingText +=”We do hope you enjoy this book as much as we enjoyed writing it.”

    注释:在执行这段代码的时候,首先会创建一个System.String类型的对象,并把它初始化为文本”Hello from all the guys at Wrox Press. ”。此时.NET运行库会为该字符串分配足够的内存来保存这个文本,完后在设置变量greeingText,表示这个字符串实例。从语法上看,下一行代码是把更多的文本添加到字符串中。实际上并非是这样的,而是创建一个新的字符串实例,给它分配足够的内存,以存储合并的文本。把最初的文本“Hello from all the guys at Wrox Press.  “复制到这个新字符串中,再加上额外的文本”We do hope you enjoy this book as much as we enjoyed writing it.“,然后更新存储在变量greetingText中的地址。使变量正确地指向新的字符串对象。而在没有引用的旧的字符串对象-不再有变量引用的时候,下一次垃圾收集器清理应用程序中所有未使用的对象时,就会删除掉它。

    下面为了展示一种简单的加密模式,用ASCII码靠后的字符串替代其中的每个字母(标点符号除外)。

         string greetingText = "Hello from all the guys at Wrox Press. ";

          greetingText += "We do hope you enjoy this book as mush as we enjoyed writing it.";

         for (int i = 'z'; i >= 'a'; i--)

          {
              char old1 = (char)i;
              char new1 = (char)(i + 1);
             greetingText = greetingText.Replace(old1, new1);
           }

          for (int i = 'Z'; i >= 'A'; i--)

          {
              char old1 = (char)i;
             char new1 = (char)(i + 1);
             greetingText = greetingText.Replace(old1, new1);
          }
          Console.WriteLine("EnCode:\n" + greetingText);
          Console.ReadLine();

    注释:代码中没有把Z换成A,也没有把z换成a只是为了简单而已,这些字符分别编码为[和{。

    在上面的案例中,greetingText字符串最后是103个字符。调用Replace()就会新分配一个新字符串,一共分配了26次,所以这个案例的加密过程需要在堆上有一个总共能存储2678个字符串对象,最终这个字符会被垃圾收集!显然,如果使用字符串频繁进行文字的处理,应用程序就会遇到严重的性能问题。

   因此,Microsoft提供了System.Text.StringBuilder类。StringBuilder类不像String类那样能够支持非常多的方法。仅限于替换、追加或删除字符串中的文本(优点:工作方式高效)。

   在使用String类构造一个字符串时,要给它分配足够的内存来保存字符串。然而,StringBuilder类通常分配的内存会比它需要的更多。我们可以选择指定StringBuilder要分配多少内存,但是如果没有指定StringBuilder要分配多少内存,在默认情况下就根据初始化StringBuilder实例时字符串长度来确定内存的大小。

   stringBuilder类有两个主要的属性:

   Length指定字符串的实际长度。

   Capacity指定字符串在分配的内存中的最大长度。

   对字符串的修改就在赋予StringBuilder实例的内存块中进行,这就大大提高了追加子字符串和替换单个字符串的效率。删除或插入子字符串仍然效率底下,因为这需要移动随后的字符串。只有执行扩展字符串容量的操作,才需要给字符串分配新内存,才能移动包含的整个字符串。在添加额外容量时候,如果StringBuilder类检测到容量超出,且容量没有设置新值,就会使自己的容量翻倍。

   例如:如果使用StringBuilder对象来加密我们的欢迎语:

   StringBuilder greetingBuilder = new StringBuilder("Hello from all the guys at Wrox Press. ", 150);

   greetingBuilder.AppendFormat("We do hope enjoy this book as much as we enjoyed writing it");

   Console.WriteLine("Not Encode:\n" + greetingBuilder);

   for (int i = 'z'; i >= 'a'; i--)

    {
        char old1 = (char)i;
         char new1 = (char)(i + 1);
         greetingBuilder = greetingBuilder.Replace(old1, new1);
      }

     for (int i = 'Z'; i >= 'A'; i--)

      {
          char old1 = (char)i;
          char new1 = (char)(i + 1);
          greetingBuilder = greetingBuilder.Replace(old1, new1);
      }
      Console.WriteLine("Encoded:\n" + greetingBuilder);

  注意:这段代码使用StringBuilder.Replace()方法,它的功能与String.Replace()一样,不要误会这个Replace是String类中的。并且这段代码中,为StringBuilder类设置的初始容量是150,在这替换期间都不会重新进行分配内存,因为其容量足够用了(StringBuilder实际理论上允许拥有的最大空间是20亿个字符空间)。

StringBuilder成员

   我们在创建字符串的时候介绍了StringBuilder类的一个构造函数,它的参数是一个初始字符串以及该字符串的容量。StringBuilder类还有几个其他的构造函数,例如,可以只提供一个字符串:

    StringBuilder sb=new StringBuilder(“Hello”);

   或者给定一个容量创建一个空的StringBuilder类:

   StringBuilder sb=new StringBuilder(20);

   我们除了要记住StringBuilder的Length和Capacity属性外,还应该知道只读属性MaxCapacity(表示对给定的StringBuilder实例的容量限制。在默认的情况下,这由int.MaxValue给定,大约是20亿)。但在实际的应用中我们用不到这么大,可以设置成我们实际需要的值,例如:

   StringBuilder sb=new StringBuilder(100,500);

  还可以显示的设置容量,但是如果把这个值设置为小于字符串的当前长度,或者超出了最大容量的某个值,就会抛出一个异常。

  StringBuilder sb=new StringBuilder(“Hello”);

  sb.Capacity=100;

  StringBuilder类的内容都在下面的MSDN链接中:

  

  注意其中的一些方法还有几种格式的重载方法。

  注意:不能把StringBuilder强制转换为String(隐式和显式转换都不行)。如果要把StringBuilder的内容输出为String,唯一的方式是使用ToString()方法。

格式化字符串

    我们常常会希望以各种可能的方式显示变量的内容,在不同的文化或地区背景中有不同的格式。.NET基类System.DateTime就是最明显的一个示例:可以把日期显示为10 June 2012、10 Jun 2012 、6/10/12(美国)、6/10/12(英国)或10.06.2012(德国)。

   首先我们做一个格式化字符串的案例简单的说明下:

   double d=13.45;

   int i = 45;

   Console.WriteLine(“The double is {0,10,E} and the int contains {1}”,d,i);

  格式化字符串本身大都由要显示的文本组成,但只要有要格式化的变量,它在参数列表中的下标就必须放在花括号中。在花括号中还可以有与该项的格式相关的其他信息。例如:可以包含:

  该项的字符串表示要占用的字符数,这个信息的前面应有一个逗号。负值表示该项应左对齐,正值表示该项应右对齐。如果该项占用的字符数比给定的多,其内容就会完整地显示出来。