C#开发训练营

第8课:文本处理

本课讨论如何在C#代码中处理文本信息,主要包括String、StringBuilder、数字格式化、GUID、散列算法等内容,并对常用资源进行了封装。

String类

C#代码中,使用一对双引号(")定义文本内容,即字符串类型数据;其中的string类型实际是System.String类的映射,也就是说,每一个字符串对象都是String类的实例。

String类处理是的不可变字符串,即当一个字符串的内容确定后就不能修改,任何改变字符串内容的操作都会生成新的字符串实例。

当两个或多个字符串连接时,可以使用加号运算符(+),如下面的代码。

C#
string s1 = "abc";
string s2 = "def";
string s = s1 + s2;
tWeb.WriteLine(s);

执行代码会显示abcdef。

字符转义

使用双引号定义的字符串中,对于一些特殊的字符需要使用外斜线(\)转义,如使用\"转义才能在字符串中定义一个双引号。常用的转义字符如图1。

图1

逐字字符串

在C#中还可以使用逐字字符串,以方便特殊字符的定义,定义的方法是在一对双引号前添加@符号。如下面两个字符串定义的内容相同的。

C#
string s1 = "c:\\list.txt";
string s2 = @"c:\list.txt";

代码中的s1和s2字符串定义的内容都是c:\list.txt,其中,s1中的\符号使用了转义,而s2字符串使用逐字字符串定义,其中的的\符号不需要转义。

逐字字符串中,唯一需要转义的是双引号,如@"""abc"""定义的内容就是"abc",其中,两端的双引号定义字符串边界,abc前后的各两个双引号分别转义为一个双引号。

Format()方法——格式化字符串

String.Format()方法可以将不同类型的数据组合成字符串形式,其中,参数一是包含了数据点位符的字符串,如第一个数据点位是{0}、第二个数据是{1},以此类推;参数二开始依次指定格式化字符串中所需的数据。下面的代码演示了String.Format()方法的基本应用。

C#
int x = 10;
int y = 99;
string s = string.Format("{0}+{1}={2}", x, y, x + y);
tWeb.WriteLine(s);

代码会显示10+99=109。

定义格式化字符串时,需要注意的是左、右花括号({和})的定义,如果需要显示一个左(或右)花括号,则需要在字符串中使用两个左(或右)花括号,如下面的代码生成的字符串内容是{0}。

C#
string.Format("{{0}}")

在数据点位符中,还可以指定数据的格式,如显示两位小数就可以{0:f2}格式,如下面的代码创建的字符串s的内容就是10.00。

C#
string s = string.Format("{0:f2}", 10);

稍后会讨论关于数字格式化的更多内容。

Split()方法——分割字符串

String.Split()方法用于分割字符串,并返回分割后的字符串。此方法有多个重载版本,下面先来看一个简单的应用。

C#
string s = "abc,def,ghi";
string[] ss = s.Split(',');
for (int i = 0; i < ss.Length; i++)
   tWeb.WriteLine(ss[i]);

代码会将s字符串通过逗号(,)分割,并将分割后的结果保存到ss数组,最后通过for循环语句显示所有成员,显示结果如图2。

图2

Split()方法也可以同时使用多个字符进行分割操作,如同时使用空格、逗号等字符分割,下面的代码演示了相关应用。

C#
string s = "abc,def ghi'jkl";
string[] ss = s.Split(new char[] { ',',' ','\'' });
for (int i = 0; i < ss.Length; i++)
    tWeb.WriteLine(ss[i]);

代码中,同时使用逗号、空格和单引号分割字符串,显示结果如图3。

图3

实际上,对于这类情况,使用正则表达式处理应该是更加高效的方式,后续课程会有相关讨论。

如果不需要分割后的空字符串成员,可以将Split()方法的第二个参数设置为StringSplitOptions.RemoveEmptyEntries,如下面的代码。

C#
string s = "abc,def,,ghi";
string[] ss = s.Split(new char[] { ','},StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < ss.Length; i++)
   tWeb.WriteLine(ss[i]);

代码执行结果如下图4。

图4

Substring()方法——截取字符串

String.Substring()方法用于截取子字符串,常用的格式为

C#
Substring(<开始截取索引>,<截取字符数>)

其中,如果不指定<截取字符数>,则从开始截取索引位置截取所有内容。下面的代码演示了String.Substring()方法的基本应用。

C#
string s = "abcdefg";
tWeb.WriteLine(s.Substring(2,3));
tWeb.WriteLine(s.Substring(2));

代码执行结果如下图5。

图5

实际应用中,String.Substring()会有一些特殊情况需要注意,如指定载取的字符数量超出了字符串全部字符数量就会产生异常,此时,可以分为两种情况来处理。

第一种情况是对原有字符串字符数量进行判断,然后进行截取,如下面的代码。

C#
string s = "abcdefghijkl";
if (s.Length < 12)
    tWeb.WriteLine("ERR:没有足够的字符可截取");
else
    tWeb.WriteLine(s.Substring(2, 10));

本例会显示cdefghijkl,如将s.Substring()方法的第二个参数设置为大于10的数值,就会产生System.ArgumentOutOfRangeException异常(索引和长度必须引用该字符串内的位置)。

第二种情况,如果对截取的字符数量要求不严格,即指定的载取字符数量超出了字符串的范围时,只截取到字符串的末尾即可。对于这种操作,可以封装为tStr.SubStr()方法,代码如下(/app_code/common/base/tStr.cs)。

C#
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Security.Cryptography;

public static class tStr
{
    // 截取指定长度字符串,如果字符数不够,则取到结尾
    public static string SubStr(this string s, int startIndex, int len)
    {
        if (s == "") return "";
        if (startIndex + len < s.Length) 
            return s.Substring(startIndex, len);
        else 
            return s.Substring(startIndex);
    }
    // 其它代码
}

tStr.SubStr()方法定义为扩展方法,可以通过String类的对象调用,如下面的代码。

C#
string s = "abcdefg";
tWeb.WriteLine(s.SubStr(2, 3));
tWeb.WriteLine(s.SubStr(2, 10));

代码执行结果如图6。

图6

其它常用方法

String类中还包括一些常用的文本操作,如Compare(s1,s2)静态方法比较两个字符串,当s1小于s2时返回值小于0,相等时返回0,s1大于s2时返回值大于0;默认情况下,Compare()方法在比较时会严格区分大小写,如果需要忽略字母大小写,可以将第三个参数设置为true。

此外,判断字符串中是否包括指定的内容时,可以使用如下实例方法:

  • Contains(s)方法,判断字符串中是否包括参数s指定的内容,找到指定内容时返回true,否则返回false。
  • IndexOf(s)方法,返回s在字符串中首次出现的索引位置,没有找到时返回-1。
  • LastIndexOf(s)方法,返回s在字符串中最后一次出现的索引位置,没有找到时返回-1。
  • StartsWith(s)方法,判断字符串是否以s开始。
  • EndsWith(s)方法,判断字符串是否以s结束。

下面的代码演示了几个方法的基本应用。

C#
using System;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string s = "abcdefg";
        tWeb.WriteLine(s.Contains("efg"));
        tWeb.WriteLine(s.IndexOf("def"));
        tWeb.WriteLine(s.StartsWith("abc"));
        tWeb.WriteLine(s.EndsWith("abc"));
    }
}

执行代码会显示True、3、True、False。

StringBuilder类

开发中,如果有大量的文本组合操作,比如,在操作数据库时需要生成SQL语句时,可以使用StringBuilder对象进行操作。

构建StringBuilder对象时,常用的构建函数版本包括:

  • StringBuilder(),创建StringBuilder对象。
  • StringBuilder(int),创建StringBuilder对象,参数用于指定初始字符数。指定StringBuilder对象一个合适的初始字符数时,可以减少内存重新分配的操作,提高内容使用效率。
  • StringBuilder(string,int),创建StringBuilder对象,参数一指定初始内容,参数二指定初始字符数。

向StringBuilder对象追加内容时,可以使用如下方法。

  • Append()方法,将参数中指定的内容以文本格式追加到现有内容后。
  • AppendLine()方法,追加参数中指定的内容,并添加换行符。
  • AppendFormat()方法,功能与string.Format()相似,但会将格式化后后的字符串追加到StringBuilder对象中。

下面的代码演示了StringBuilder类的基本应用。

C#
using System;
using System.Text;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        int x = 10;
        int y = 99;
        StringBuilder sb = new StringBuilder(256);
        sb.Append("加法演示:");
        sb.AppendFormat("{0}+{1}={2}", x, y, x + y);
        tWeb.WriteLine(sb.ToString());
    }
}

代码执行结果如图7。

图7

此外,StringBuilder中的常用方法还包括:

  • Insert()方法,参数一指定插入的索引位置,参数二指定插入的内容。
  • Remove()方法,参数一指定开始删除的索引位置,参数二指定删除的字符数。
  • Replace()方法,参数一指定需要替换的内容,参数二指定新的内容。
  • Clear()方法,清除StringBuilder对象中的内容。
  • ToString()方法,将StringBuilder对象中的内容转换为string类型。

数字格式化

数字的呈现有多种形式;代码中,格式化字符串时可以通过如下字符指定数字的格式:

  • C或c,货币格式。
  • D或d,整数。
  • E或e,指数计数法。
  • F或f,浮点数。可以指定小数位。
  • G或g,科学计算数。
  • N或n,数值。
  • P或p,百分数。
  • R或r,可以往返至相同数字的字符串。
  • X或x,十六进制字符串。其中,使用X时字母大写,使用x时字母小写。

下面的代码演示了其中一些格式化字符的应用。

C#
using System;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        double x = 123456.789;
        tWeb.WriteLine(string.Format("{0:c}", x));
        tWeb.WriteLine(string.Format("{0:e}", x));
        tWeb.WriteLine(string.Format("{0:f2}", x));
        tWeb.WriteLine(string.Format("{0:p1}", 0.25));
        tWeb.WriteLine(string.Format("{0:x}", 126));
    }
}

代码执行结果如图8。

图8

tStr类

本节讨论的内容封装在tStr类中,其中包含了大量的字符串相关操作。

GUID

GUID会通过一定算法生成唯一的ID,项目中如果需要为资源创建全球唯一的ID,就可以考虑使用GUID。下面的代码定义了tStr.NewGuid()方法,用于返回只包含字母和数字的GUID字符串。

C#
public static string NewGuid()
{
    return Guid.NewGuid().ToString("N");
}

代码中使用了Guid.NewGuid().ToString()方法,其中使用了"N"参数,用于返回只包含字母和数字的GUID字符串,长度为32个字符;相关的参数还包括:

  • D,由连接字符分隔的GUID,格式如00000000-0000-0000-0000-000000000000。
  • B,包含在在大括号中、由连接字符分隔的GUID,格式如{00000000-0000-0000-0000-000000000000}。
  • P,包含在在圆括号中、由连字符分隔的GUID,格式如(00000000-0000-0000-0000-000000000000)。
  • X,包含在大括号的4个十六进制值,其中第4个值是包含在大括号中由8个十六进制值组成的子集,格式如{0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}。

开发中,如果需要GUID创建PNG图片文件名,就可以使用如下代码。

C#
string filename = tStr.GetGuid() + ".png";

散列算法

常用的散列算法包括MD和SHA等,在C#代码中也可以将字符串转换为散列算法,接下来会在tStr类中封装相关功能。首先来看几个辅助方法,如下面的代码。

C#
//将字符串转换为字节数组
public static byte[] ToBytes(string s)
{
    try { return Encoding.Default.GetBytes(s); }
    catch { return null; }
}

// 将字节内容还原成字符串
public static string ToStr(byte[] bytes)
{
    try { return Encoding.Default.GetString(bytes); }
    catch { return ""; }
}
//将字节数组内容以十六进制字符串显示
public static string ToHexStr(byte[] bts)
{
    try
    {
        StringBuilder result =
            new StringBuilder(bts.Length * 2);
        for (int i = 0; i < bts.Length; i++)
        {
            result.Append(bts[i].ToString("X2"));
        }
        return result.ToString();
    }
    catch { return ""; }
}

代码中共定义了三个方法,分别是:

  • ToBytes()方法,将字符串转换为字节数组。
  • ToStr()方法,将字节数组转换为字符串。
  • ToHexStr()方法,将字节数组转换为对应的十六进制表示的字符串,每字节使用2位十六进制表示。

接下来是几个将字符串转换为MD和SHA散列编码的方法。

C#
//
public static string Md5(this string s)
{
    byte[] bytes = ToBytes(s);
    using (MD5CryptoServiceProvider csp =
        new MD5CryptoServiceProvider())
    {
        byte[] hash = csp.ComputeHash(bytes);
        return ToHexStr(hash);
    }
}
//
public static string Sha1(this string s)
{
    byte[] bytes = ToBytes(s);
    using (SHA1CryptoServiceProvider csp =
        new SHA1CryptoServiceProvider())
    {
        byte[] hash = csp.ComputeHash(bytes);
        return ToHexStr(hash);
    }
}
//
public static string Sha256(this string s)
{
    byte[] bytes = ToBytes(s);
    using (SHA256CryptoServiceProvider csp =
        new SHA256CryptoServiceProvider())
    {
        byte[] hash = csp.ComputeHash(bytes);
        return ToHexStr(hash);
    }
}
//
public static string Sha384(this string s)
{
    byte[] bytes = ToBytes(s);
    using (SHA384CryptoServiceProvider csp =
        new SHA384CryptoServiceProvider())
    {
        byte[] hash = csp.ComputeHash(bytes);
        return ToHexStr(hash);
    }
}
//
public static string Sha512(this string s)
{
    byte[] bytes = ToBytes(s);
    using (SHA512CryptoServiceProvider csp =
        new SHA512CryptoServiceProvider())
    {
        byte[] hash = csp.ComputeHash(bytes);
        return ToHexStr(hash);
    }
}

代码中实现了一系列的string类型的扩展方法,包括:

  • Md5()方法,将字符串转换为MD5编码的文本形式,返回内容包含32个字符。
  • Sha1()方法,将字符串转换为SHA-1编码的文本形式,返回内容包含40个字符。
  • Sha256()方法,将字符串转换为SHA-256编码的文本形式,返回内容包含64个字符。
  • Sha384()方法,将字符串转换为SHA-384编码的文本形式,返回内容包含96个字符。
  • Sha512()方法,将字符串转换为SHA-512编码的文本形式,返回内容包含128个字符。