码迷,mamicode.com
首页 > 其他好文 > 详细

第14章 字符、字符串和文本处理

时间:2016-06-26 19:40:59      阅读:144      评论:0      收藏:0      [点我收藏+]

标签:

14.1字符

在.Net Framework中,字符总是表示成16位Unicode代码值,这简化了国际化应用程序的开发。

每个字符都表示成System.Char结构的一个实例。

针对Char的一个实例,可以调用静态GetUnicodeCategory方法,这个方法返回的是System.Globalization.UnicodeCategory枚举类型的一个值。

Char类型提供了几个静态方法,比如IsDigit,IsUpper等。注意,所有这些方法要么获取单个字符作为参数,要么获取一个String以及目标字符在这个String中的索引作为参数。
ToLower和ToUpper之所以需要语言文化信息,是因为字母的大小写转换是一种依赖于语言文化的操作。语言文化信息是这两个方法在内部查询System.Threading.Thread类的静态CurrentCulture属性获取的。

除了这些静态方法,Char类型还提供了几个实例方法。比如:Equals方法会在两个Char实例代表同一个16位Unicode码位的前提下返回true。CompareTo方法返回两个Char实例忽略语言文化的比较结果。GetNumericValue方法,它返回字符的数值形式,以下代码演示了这个方法。

    public static class Program {

        public static void Main() {

            Double d;

            d = Char.GetNumericValue(‘3‘);//3

            Console.WriteLine(d.ToString());

            d = Char.GetNumericValue(‘A‘);//-1

            Console.WriteLine(d.ToString());

        }

}

可以使用三种技术实现各个数值类型与Char实例的相互转换。

  • 转型(强制类型转换)要将Char转换成一个数值,最简单的方法就是转型。
  • 使用Convert类型 System.Convert类型提供了几个静态方法来实现Char和数值类型的相互转换。所有这些方法都以checked的方法来执行转换。所以一旦发现转换会造成数据丢失,就会抛出一个OverflowException异常。
  • 使用IConvertible接口这种技术效率太差,因为在值类型上调用一个接口方法,要求对实例进行装箱-Char和所有数值类型都是值类型

以下代码演示了如何使用者三种技术:

public static class Program

    {

        public static void Main()

        {

            Char c;

            Int32 n;

            //使用C#强制类型转换

            c = (Char)65;

            n = (Int32)c;

            //使用Convert类型

            c = Convert.ToChar(65);

            try

            {

   //700000000000对于Char的16位来说过大

                c = Convert.ToChar(700000000000);

            }

            catch (OverflowException)

            {

                Console.WriteLine("Cannot convert 700000000000 to a Char");

            }

            //使用IConvertible接口

            c = ((IConvertible)65).ToChar(null);

 

        }

    }

14.2 System.String类型

一个String代表一个不可变(immutable)的顺序字符集。String类型直接派生自Object,所以它是一个 引用类型。因此,String对象永远存在于堆上,永远不会跑到线程栈。

14.2.1构造字符串

C#将String视为一个基元类型-也就是说,编译器允许在源代码中直接表示文本常量字符串。编译器将这些文本常量字符串放到模块的元数据中,并在运行时加载和引用它们。

在C#中,不能使用new操作符从一个文本常量字符串构造一个String对象,必须使用简化过的语法。

    public static class Program

    {

        //错误

        String s = new String("Hi");

        //正确

        String s1 = "Hi";

    }

对于换行符、回车符和退格符这样的特殊字符,C#采用转义机制。

\r return 回车

\n newline 换行

            //包含回车换行符的字符串

            String s = "Hi\r\nthere";

            //以下是定义上述字符串的正确方式

            String s1 = "Hi" + Environment.NewLine + "there";

可以使用C#的+操作符将几个字符串连接成一个。String s2 = "Hi" + "" + "there";

在上述代码中,由于所有字符串都是文本常量字符串,所以C#编译器会在编译时连接它们,最终只会将一个字符串(即"Hi there")放到模块的元数据中。对非文本常量字符串使用+操作符,连接则会在运行时进行。若要在运行时将几个字符串连接到一起,请避免使用+操作符,因为它会在堆上创建多个字符串对象,而堆是需要垃圾回收的,从而影响到性能。相反,应尽量使用String.Text.StringBuilder类型。

C#还提供了“逐字字符串(verbatim strings)”声明方式,通常用于指定文件或目录的路径,或者与正则表达式配合使用。

            //不使用逐字字符串字符@来声明字符串

            String file = "C:\\Windows\\System32\\Notepad.exe";

            //使用逐字字符串字符@来声明字符串

            String file = @"C:\Windows\System32\Notepad.exe";

在字符串之前添加@符号,是编译器知道字符串是一个逐字字符串。事实上,这告诉编译器将反斜杠字符视为文本常量,而不是转义字符,使文件路径在源代码中更易读。

14.2.2字符串是不可变的

String对象最重要的一个事实就是,它使不可变的。也就是字符串一经创建便不能更改,不能变长变短或修改其中的任何字符。

14.2.3比较字符串

一般会出于两方面的原因来比较字符串:

  • 判断相等性
  • 对字符串进行排序

进行排序时,应该总是执行区分大小写的比较。

Compare方法中ignoreCase设为true,不区分大小写。

判断字符串相等性或对字符串进行排序时,强烈建议调用下面的方法之一:

public bool Equals(string value, StringComparison comparisonType);

public static bool Equals(string a, string b, StringComparison comparisonType);

public static int Compare(string strA, string strB, StringComparison comparisonType);

public static int Compare(string strA, string strB, bool ignoreCase, CultureInfo culture);

public static int Compare(string strA, string strB, CultureInfo culture, CompareOptions options);

public static int Compare(string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType);

public static int Compare(string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options);

public static int Compare(string strA, int indexA, string strB, int indexB, int length, bool ignoreCase, CultureInfo culture);

public bool StartsWith(string value, StringComparison comparisonType);

public bool StartsWith(string value, bool ignoreCase, CultureInfo culture);

public bool EndsWith(string value, StringComparison comparisonType);

public bool EndsWith(string value, bool ignoreCase, CultureInfo culture);

许多程序都将字符串用于内部编程目的,比如路径名、文件名、URL、注册表项/值、环境变量、反射、XML等。出于编程目的而比较字符串时,应该总是使用StringComparison.Ordinal,这是执行字符串比较时最快的一种方式,因为在执行比较时,不需要考虑语言文化信息。

从现在起我们将讨论如何执行在语言文化上正确的比较。.Net Framework使用System.Globalization.CultureInfo表示一个“语言/国家”。

以下代码演示了序号比较和依赖语言文化比较的区别:

static void Main()

        {

            String s1 = "Strasse";

            String s2 = "Straße";

            Boolean eq;

            //Compare返回非零值,如果传递Ordinal标志,Compare方法会忽略指定的语言文化

            eq = String.Compare(s1, s2, StringComparison.Ordinal) == 0;

            Console.WriteLine("Ordinal comparison:‘{0}‘{2}‘{1}‘", s1, s2, eq ? "==" : "!=");

            //面向在德国说德语的人群

            CultureInfo ci = new CultureInfo("de-DE");

            //Compare返回零值

            eq = String.Compare(s1, s2, true, ci) == 0;

            Console.WriteLine("Cultural comparison:‘{0}‘{2}‘{1}‘", s1, s2, eq ? "==" : "!=");

        }

14.2.4字符串留用

如上一节所述,检查字符串的相等性是许多应用程序的常见操作 - 这个任务可能严重损害性能。

执行序号ordinal相等性检查时,CLR快速检查两个字符串是否具有数量相同的字符。如果答案是肯定的,字符串有可能相等。然后CLR必须比较每个单独的字符才能确定。

除此之外,如果在内存中复制同一个字符串的多个实例,会造成内存的浪费,因为字符串是不可变的。如果只在内存中保留字符串的一个实例,那么将显著提高内存的利用率。需要引用字符串的所有变量只需指向单独一个字符串对象。

如果应用程序经常对字符串进行区分大小写的、序号式的比较,或者事先知道许多字符串对象都有相同的值,就可利用CLR的字符串留用(string interning)机制来显著提高性能。

CLR初始化时会创建一个内部哈希表,在这个表中,键(key)是字符串,而值(value)是对托管堆中的String对象的引用。

String类提供了两个方法,便于你访问这个内部哈希表:

public static string Intern(string str);

public static string IsInterned(string str);

Equals和ReferenceEquals的区别:

  • ReferenceEquals:  永远比较2个引用对象所指向的地址是否相同,是比较引用。Object的静态方法,因此不能在继承类中重写该方法。判断值类型的时候需要装箱,肯定返回false,因为使用ReferenceEquals(object a,object b)方法后值类型被重新装箱为新的引用类型实例。String 类型因为有字符串驻留机制: string A="a"; string B="a"; 返回为True.
  • Equals:永远比较2个对象 (无论引用,非引用) 的值是否相等,是比较值。

 

以下代码演示了字符串留用:

        static void Main()

        {

            String s1 = "Hello";

            String s2 = "Hello";

            Boolean a = Object.ReferenceEquals(s1, s2);//true

            s1 = String.Intern(s1);

            s2 = String.Intern(s2);

            Boolean b = Object.ReferenceEquals(s1, s2);//true

        }

在对ReferenceEquals方法的第一个调用中,在CLR低版本中,s1引用堆中“Hello”字符串对象,而s2引用堆中另一个“Hello”字符串对象。在CLR的4.0版本上运行时,CLR选择忽视C#编译器生成的attribute/flag。但程序集加载到AppDomain中时,CLR会对文本常量字符串“Hello”进行默认留用。结果就为True。

在对ReferenceEquals方法的第二个调用之前,“Hello”字符串被显示留用,s1现在引用一个已留用的“Hello”。然后,通过再次调用Intern,s2被设置成s1引用的同一个“Hello”字符串。现在,当第二次调用ReferenceEquals时,就能保证获得一个True的结果,不管程序集在编译时是否设置了attribute/flag。

14.2.5字符串池

编译器有将单个字符串的多个实例合并成一个实例的能力。

14.2.6检查字符串的字符和文本元素

14.2.7其他字符串操作

还可以利用String类型提供的一些方法来复制一个字符串或者一部分。

public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count);

ToString:返回对同一个对象的引用

上述方法中将字符串的部分字符复制到字符数组中。

使用所有这些方法时都请牢记一点,它们返回的都是一个新的字符串对象。

14.3高效率构造字符串

由于String类型代表的是一个不可变字符串,所以FCL提供了另一个名为System.Text.StringBuilder的类型。可利用它高效地对字符串进行动态处理,最后基于处理结果创建一个String。

从逻辑上说,StringBuilder对象包含一个字段,该字段引用了由Char结构构成的一个数组。可利用StringBuilder的成员来操纵这个字符数组,高效地缩短字符串或更改字符串中的字符。

 

14.3.1构造StringBuilder对象

 技术分享

使用StringBuilder的方法时要记住,大多数方法返回的都是对同一个StringBuilder对象的引用。所以可以方便的将几个操作链接到一起完成:

        static void Main()

        {

            StringBuilder sb = new StringBuilder();

     String s = sb.AppendFormat("{0} {1}", "Jeffrey", "Richter").Replace(‘ ‘, ‘-‘).Remove(4, 3).ToString();

        }

拼接字符串示例:

            String[] value = { "1", "2", "3" };

            String a = "";

            StringBuilder str = new StringBuilder();

            foreach (String text in value)

            {

                str.AppendFormat(",{0}", text);//将value数组中的值拼接成一个字符串,以逗号分隔

            }

            if (str != null && str.Length > 0)

            {

                str.Remove(0, 1);//移除第一个逗号

            }

            a = str.ToString();//要将StringBuilder转换成字符串

 

String和StringBuilder类提供的方法并不是完全匹配的。例如:String提供了ToLower,ToUpper,EndsWith,Trim等方法,但StringBuilder类没有提供任何与之对应的方法。另一方面,StringBuilder类提供了一个功能更全面的Replace方法,它允许替换作为一个字符串的一部分字符。而String类中Replace方法是public string Replace(char oldChar, char newChar);

由于这两个类中的方法不完全对应,所以有时需要在String和StringBuilder转换来完成特定的任务。

            StringBuilder sb = new StringBuilder();

            String s = sb.AppendFormat("{0}, {1}", "Jeffrey", "Richter").ToString();

            s.ToUpper();

            sb.Length = 0;

            sb.Append(s).Insert(8, "Marc-");

            s = sb.ToString(1, 2);

14.4获取对象的字符串表示:ToString(重看)

我们经常都要获取一个对象的字符串表示。可以调用ToString方法来获取任何对象的字符串表示。

无参ToStirng方法有两个问题。

String的Format方法。。。

14.5解析字符串来获取对象: Parse

解析字符串来获取一个对象,偶尔会用到。

Int32 x = Int32.Parse("1A", NumberStyles.HexNumber);//26

能解析一个字符串的任何类型都提供了Parse的一些public static方法。

先来看看如何将一个字符串解析成数值类型:

public static int Parse(string s, NumberStyles style, IFormatProvider provider);

s是字符串参数,NumberStyles是字符串参数s中运行的样式

Int32 x = Int32.Parse(" 123", NumberStyles.None); //要解析的字符串包含一个前导空白字符,会报FormatExpection异常

应该设成NumberStyles.AllowLeadingWhite

14.6 编码:字符和字节的相互转换

14.7安全字符串

Microsoft在FCL中添增了一个更安全的字符串类System.Security.SecureString

第14章 字符、字符串和文本处理

标签:

原文地址:http://www.cnblogs.com/chrisghb8812/p/5618299.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!