Web应用程序在浏览器中显示字符串时,由于显示长度的限制,常常需要将字符串截取后再进行显示。但目前很多流行的语言,如C#、Java内部采用的都是Unicode 16(UCS2)编码,在这种编码中所有的字符都是两个字符,因此,如果要截取的字符串是中、英文、数字混合的,就会产生问题,如下面的字符串:
String s = "a加b等于c,如果a等1、b等于2,那么c等3";
上面的字符串既有汉字,又有英文字符和数字。如果要截取前6个字节的字符,应该是”a加b等",但如果用Substring方法截取前6个字符就成了"a加b等于c"。产生这个问题的原因是将Substring方法将双字节的汉字当成一个字节的字符(UCS2字符)处理了。 要解决这个问题的方法是首先得到该字符串的UCS2编码的字节数组,如下面的代码如下:
byte[] bytes = System.Text.Encoding.Unicode.GetBytes(s);
然后从第一个字节开始扫描,对于一个英文或数字字符,UCS2编码的第一个字节是相应的ASCII,第二个字节是0,如a的UCS2编码是97 0,而汉字两个字节都不为0,因此,可以利于UCS2编码的这个规则来计算实际的字节数,为了更方便,将按字节长度截取字符串的方法注册为String类的扩展方法,实现代码如下:
public static class StringExt { /// <summary> /// 格式化超过指定长度的字符串,显示截取后字符串加... /// </summary> /// <param name="str">字符串</param> /// <param name="displayLength">能显示的字节长度</param> /// <returns></returns> public static string FormatStringLength(this string str, int displayLength) { //截取后的字符串 string subStr = string.Empty; //字符串生成的默认编码的字节长度 int nameLenth = Encoding.Default.GetByteCount(str); //字符串字节长度大于能显示的字节长度,进行截取 if (nameLenth > displayLength) { //减去将要附加到尾部的"..."的长度,得到要截取的字节长度 displayLength = displayLength - 3; //当前遍历到的字节数,是按displayLength计算字节数的, //即汉字算两个字节,英文数字等一个,用来与displayLength比较,好退出循环 int CurrentLength = 0; //要截取的字节长度,该长度不同于displayLength,这里是Unicode(USC2)编码, //不区分汉字还是字母,每个字符占两个字节长度 int subLength = 0; //字符串生成的Unicode(USC2)编码的字节数组 byte[] strBytes = Encoding.Unicode.GetBytes(str); // for (; subLength < strBytes.GetLength(0) && CurrentLength < displayLength; subLength++) { //因为Unicode(USC2)编码时,不区分汉字还是字母,每个字符占两个字节长度, //这里subLength做下标,为0或每次为偶数时,正好是UCS2编码中两个字节的第一个字节, //对于一个英文或数字字符,UCS2编码的第一个字节是相应的ASCII,第二个字节是0,如a的UCS2编码是97 0,而汉字两个字节都不为0 //除2的余数为0,表示这是每个字符的第一个字节,字母数字等只在这里对CurrentLength加1,汉字等则在第二个字节处判断出后再加1 if (subLength % 2 == 0) { CurrentLength++; } else//除2的余数不为0,表明是一个字符的第二个字节,检查字符的第二个字节 { //汉字需要再加上1,以符合默认编码占两个字节 if (strBytes[subLength] > 0) { CurrentLength++; } } } //如果subLength为奇数时,即截取的最后一个字符,两个字节中只截取了1个即一般,需处理成偶数 if (subLength % 2 == 1) { //对字符的第二个字节进行判断(使用自身做下标,因为下标从0开始,实际检查的就是自己后面的一个字节) //该UCS2字符是汉字时,第二个字节在默认编码中占1个字节,补全的话,长度超限,所以去掉这个截一半的汉字 if (strBytes[subLength] > 0) { subLength = subLength - 1; } else//该UCS2字符是字母或数字,第二个字节在默认编码中不存在,并不占空间,补全该字符 { subLength = subLength + 1; } } subStr = Encoding.Unicode.GetString(strBytes, 0, subLength) + "..."; } else//长度未超限,不作格式化 { subStr = str; } return subStr; } }
在上面的代码中,如果最后要截取奇数个字符(以字节为单位),并且当最后一个字符是字母或数字,则保留该字符,如果是汉字,说明这个汉字被截了一半,则去掉这个汉字。
可以使用下面的代码来截取字符串:
string subStr = s.FormatStringLength(6); //substr的值是"a加b等"