前面,我们已经了解了string类型的字符串,它是System.String类型的别名,定义为不可变字符串类型,也就是说,当一个字符串的内容确定以后就不能再修改了,如果对字符串内容进行操作就会重新分配内存来保存新的字符串对象,这样以来,对于字符串的处理效率就不会太高。本课,我们将讨论几个关于字符串处理的主题,主要包括String类、StringBuilder类、字符串格式化、正则表达式,以及如何使用扩展方法来封装代码等。

String类

我们知道,C#中的string类型实际上就是System.String类的别名,这样就可以使用String类的成员来操作字符串了,下面,我们了解String类中属性和方法的基本应用。

首先,我们使用Length属性可以获取字符串中的字符数量,使用Split()方法可以分割字符串,下面的代码演示了这两个成员的使用。

static void Main(string[] args)
{
        string s = "abc,def,ghi";
        Console.WriteLine("字符数:{0}",s.Length);
        string[] arr = s.Split(',');
        for (int i = 0; i < arr.Length; i++)
        {
            Console.WriteLine(arr[i]);
        }
}

代码执行结果如下图所示。

enter image description here

请注意Split()方法的参数,这里使用了一个逗号(char类型)作为分割字符,而在实际应用中还可以使用更多的分割方法,可以参考MSDN文档灵活应用;String类中的基本成员的应用同样可以参考MSDN文档。

StringBuilder类

StringBuilder类定义在System.Text命名空间,用于对文本内容进行高效的操作,如组合、修改、替换等操作。

对文本内容进行组合操作时,StringBuilder对象就比String对象高效;如下面的代码,我们将多个字符串内容进行组合,基中使用了Append()和AppendFormat()方法,它们可以将多种类型的数据添加到对象中,并组合为文本内容。

using System;
using System.Text;

namespace ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("abc");
            sb.AppendFormat(",{0},{1}","def","ghi");
            Console.WriteLine(sb.ToString());
        }
    }
}

本例中,我们使用了StringBuilder类的无参数构造函数,实际上,还有一些比较常用的构造函数版本可以使用,如:

  • StringBuilder(int),参数用于指定StringBuilder对象初始尺寸(可保存的字符数),有利于合理分配内存空间。
  • StringBuilder(string,int),参数一用于指定初始值,参数二用于指定对象的初始尺寸。

由于StringBuider对象在创建时已经分配了初始尺寸,所以,在对其内容操作的效率就会比String对象高;在连接多个字符串,或者是将其他类型数据组合成字符串时,使用Append()和AppendFormat()方法是非常方便的,如下面的代码,我们将int类型的数据添加到StringBuilder对象中。

StringBuilder sb = new StringBuilder("abc",32);
sb.AppendFormat("中有{0}个字符",sb.Length);
Console.WriteLine(sb.ToString());

下面,再来了解StringBuilder类中几个常用的方法。

static void Main(string[] args)
{
        StringBuilder sb = new StringBuilder("def",128);
        sb.Insert(0, "abc,");
        Console.WriteLine(sb.ToString());
        sb.Replace("def", "xyz");
        Console.WriteLine(sb.ToString());
}

本例,我们使用了StringBuilder对象的两个方法,分别是:

  • Insert()方法,用于在对象指定的位置插入新内容,参数一指定插入的位置,使用从0到始的索引值;参数二指定插入的内容,可以是各种类型的数据。
  • Replace()方法,将原文本内容中,参数一指定的内容变为参数二指定的内容。

这两个方法都有一些重载版本,大家可以参数MSDN文档使用。实际开发中,还可以参考StringBuilder对象的更多成员进行操作,从而更高效地处理文本的操作。

字符串格式化

前面,我们使用过的Console.WriteLine()方法、StringBuilder对象中的AppendFormat()方法,都可以使用参数进行文本的组合操作,实际上,string.Format()方法也可以完成同样的任务,只是它们的使用场景不一样,接下来,我们以string.Format()方法为例来讨论格式化输入字符串的问题。

String.Format()方法的第一个参数是一个字符串,其中可以使用{0}、{1}等标识指定数据的参数的顺序,即{0}为方法的第二个参数,{1}为方法的第三个参数,以此类推;这里,除了使用数据索引,还可以使用一个字母指定数据的格式,如:

  • C字符,显示为货币格式(当前系统设置)。
  • D字符,显示整数,并指定显示的位数。
  • E字符,显示科学计数法格式。
  • F字符,显示为浮点数格式,并可指定小数的位数。
  • N字符,使用数字分隔。
  • P字符,显示百分比格式。
  • X字符,显示为十六进制格式;使用x时,a至f显示为小写形式。

在这些格式化字符串中,除了X的大小写输出有区别,其他的字符是不区分大小写,下面的代码演示了字符串格式输出的一些操作。

static void Main(string[] args)
{
        Console.WriteLine("{0:C}",1.23);
        Console.WriteLine("{0:D2}",123);
        Console.WriteLine("{0:D5}", 123);
        Console.WriteLine("{0:F2}",1);
        Console.WriteLine("{0:F2}", 1.225);
        Console.WriteLine("{0:N}",123456789.0123);
        Console.WriteLine("{0:P2}",0.1);
        Console.WriteLine("{0:P2}",0.125);
        Console.WriteLine("{0:X}", 15);
        Console.WriteLine("{0:x}", 15);
}

代码执行结果如下图所示。

enter image description here

正则表达式

简单的说,正则表达式是通过模式(pattern)来匹配(match)文本内容,并可以进一步操作,如判断文本格式、查找和替换内容等。正则表达式的相关资源定义在System.Text.RegularExpressions命名空间,请注意在代码中引用。

使用正则表达式时,模式的定义是很关键的,也是难度比较大的操作,我们先通过一个示例来了解正则表达式的基本操作方法;下面的代码,我们判断一个字符串的内容是否为6位数字(如邮政编码或县级行政区划代码)。

using System;
using System.Text.RegularExpressions;

namespace ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string p = "[0-9]{6}?";
            Console.WriteLine(Regex.IsMatch("123456",p));
            Console.WriteLine(Regex.IsMatch("12345",p));
            Console.WriteLine(Regex.IsMatch("abcdef", p));
        }
    }
}

代码输出结果如下图所示。

enter image description here

本例中,首先使用”[0-9]{6}?”定义了一个模式,其中有两个部分,第一部分指定了匹配的内容,[0-9]表示0到9数字中一个;第二部分指定内容出现的规则,{6}?表示前面的内容刚好出现6次。实际应用中,匹配内容中还可以使用转义字符(可以参考字符串相关课程);如果不指定出现规则,则内容默认匹配一次。

下面给出一些常用的内容匹配方式。

  • [],匹配其中的一个字符,如[yn]匹配y或n、[a-z]匹配字母a到z、[0-9]匹配0到9的数字。
  • [^],不包含^后指定的字符。
  • .字符,匹配换行符(\n)以外的所有字符。
  • \w,匹配大小写字母、数字和下画线。
  • \W,匹配大小写字母、数字和下画线以外的字符。
  • \s,匹配空白字符,如空格、制表符等。
  • \S,匹配空白字符以外的字符(可见字符)。
  • \d,匹配十进制数字。
  • \D,匹配十进制数字以外的字符。

下面是一些常用的应用规则。

  • ^,指定匹配的内容在字符串的开始部分。
  • $,指定匹配内容在字符串的结尾部分,或换行符(\n)前。
  • *,匹配前一内容0次或多次。
  • +,匹配前一内容1次或多次。
  • ?,匹配前一内容0次或1次。
  • {n},匹配前一内容n次。
  • {n,},匹配前一内容最少n次。
  • {n,m},匹配前一内容在n到m次之间。
  • {n}?,匹配前一内容正好n次。
  • {n,}?,匹配前一内容至少n次,但尽可能少。
  • {n,m}?,匹配前一内容n到m次,但尽可能多。

正则表达式的功能是非常强大的,合理地应用正则表达式也可以让大量的文本操作事半功倍,但熟练掌握则需要大量的思考、实践和总结;下面,我们会封装几个关于字符串操作的方法,其中就包括正则表达式的应用。

使用扩展方法封装代码

首先,我们定义一个静态类CStr,其中定义一个Combine()方法,如下面的代码。

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace ConsoleTest
{
    public static class CStr
    {
        // 组合文本内容
        public static string Combine(this string s,params object[] args)
        {
            return string.Format(s, args);
        }
        //
    }
}

这个代码看起来并不复杂,只是对string.Format()方法进行了封装,下面,我们再看如何使用这个方法。

namespace ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string s = "{0},{1}".Combine("abc", 123);
            Console.WriteLine(s);
        }
    }
}

只要引用了CStr类所在的命名空间,我们就可以直接在string对象中使用Combine()方法,为什么呢?实际上,我们在定义CStr.Combine()方法时需要注意几个地方,一是CStr类定义为静态的,这样,它的成员也都应该定义为静态的;而Combine()方法的第一个参数使用了this关键字和string类型,这就会将Combine()方法定义为string类型的扩展方法。

使用扩展方法可以更方便的封装项目中所需要的常用代码,使用起来也更加方便,如按顺序连接字符串和不同类型数据的操作就可以封装为CStr.Append()方法,如下面的代码。

// 连接字符串和基本数据类型
public static string Append(this string s, params object[] objs)
{
    StringBuilder sb = new StringBuilder(s, 128);
     for (int i = 0; i < objs.Length; i++)
         sb.Append(objs[i]);
     return sb.ToString();
 }

下面,我们再封装两个关于正则表达式的应用。

    // 判断是否为电子邮件地址
    public static bool IsEmail(this string addr)
    {
        string p = @"^[a-zA-Z0-9](\w+\.)*\w+@(\w+\.)+\w+$";
        return Regex.IsMatch(addr, p);
    }
    // 判断是否是11位手机号
    public static bool IsMobile(this string num)
    {
        string p = @"1[0-9]{10}?";
        return Regex.IsMatch(num, p);
    }

下面的代码演示了CStr类中Append()方法、IsEmail()方法和IsMobile()方法的使用。

static void Main(string[] args)
{
        Console.WriteLine("abc".Append(",def",123,"***"));
        Console.WriteLine("xxx@yyy.zzz".IsEmail());
        Console.WriteLine("123@163.com".IsEmail());
        Console.WriteLine("abc.xyz".IsEmail());
        Console.WriteLine("12345678910".IsMobile());
        Console.WriteLine("123456789".IsMobile());
        Console.WriteLine("21345678910".IsMobile());
        Console.WriteLine("a2345678910".IsMobile());
}

代码执行结果如下图所示。

enter image description here

请注意,手机号码和电子邮件只能判断其格式,如果需要在项目中判断其有效性,只能通过实际联系来验证,比如常用的方法——给用户邮箱发个验证邮件。

本课,我们讨论了关于字符串和正则表达式常用的操作资源,大家可以在学习和实践中不断积累自己的代码库,并进行合理的封装,以便在项目中灵活、快速地实现所需要的功能。

CHY软件小屋原创作品!