不区分汉字还是英文和数字,按指定字节长度截取字符串

  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等"
原文地址:https://www.cnblogs.com/qfcndtt/p/2827801.html