在C#语言中,按字符数截取字符串可采用String类的SubString方法实现,但很多情况下,我们需要按字节数截取字符串。
举例来说,现有一行文字,屏幕上需要展示的界面宽度有限,但文字的总长度可能比界面的宽度要长,这就导致了屏幕上显示不下所有的字,为解决这一问题,有一个办法是只显示字符串开始的部分,然后将后面显示不下的部分替换为省略号(...)。
但是,因为全角字符和半角字符的长度是不一样的,我们不能按字符数截取字符串。如下图所示,字符串“1234567890”和“一二三四五六七八九〇”,长度都是10,但后者占据的实际长度是前者的两倍!(如果采用的不是等宽字体,那长度的偏差会千奇百怪,本文中的DEMO采用的都是Windows控制台默认的点阵字体)
一个半角字符占一个字节,一个全角字符占两个字节,而全角字符在显示上又是半角字符的两倍。 如果我们可以按字节数截取字符串,就可以保证截取字符串的长度了。
我先实现了下面这个函数,对字符串进行裁剪。
/// <summary>
/// 裁减字符串 - 直接裁减
/// </summary>
/// <param name="originalText">被裁减字符串</param>
/// <param name="bytesAfterCut">需保留的字节数</param>
/// <returns></returns>
public static string GetTreatedText(string originalText, int bytesAfterCut)
{
string treatedText = originalText;
byte[] val = Encoding.Default.GetBytes(originalText);
if (val.Length > bytesAfterCut)
{
treatedText = Encoding.Default.GetString(val, 0, bytesAfterCut) + "...";
}
return treatedText;
}
但这个方法有一个致命的漏洞,即全角字符占据两个字节,如果被裁剪字符串时,下剪的位置恰好将一个全角字符减成两半,那显示出来的字符串的最后面会出现一个问号(?)。
如下面的程序,对字符串“你好吗”进行裁剪,要求保留5个字节。但“吗”字被保留了半个字节后,就无法正确显示了。
因此,我们需要对裁剪方法做一下优化,代码如下:
/// <summary>
/// 裁减字符串 - 优化版 liwh - 20160523
/// </summary>
/// <param name="originalText">被裁减字符串</param>
/// <param name="bytesAfterCut">需保留的字节数</param>
/// <returns></returns>
public static string GetOptimizedText(string originalText, int bytesAfterCut)
{
string optimizedText = originalText;
byte[] val = Encoding.Default.GetBytes(originalText);
if (val.Length > bytesAfterCut)
{
int left = bytesAfterCut / 2;
int right = bytesAfterCut;
left = left > originalText.Length ? originalText.Length : left;
right = right > originalText.Length ? originalText.Length : right;
while (left < right - 1)
{
int mid = (left + right) / 2;
if (Encoding.Default.GetBytes(originalText.Substring(0, mid)).Length >
bytesAfterCut)
{
right = mid;
}
else
{
left = mid;
}
}
byte[] rightVal = Encoding.Default.GetBytes(originalText.Substring(0, right));
if (rightVal.Length == bytesAfterCut)
{
optimizedText = originalText.Substring(0, right) + "...";
}
else
{
optimizedText = originalText.Substring(0, left) + "...";
}
}
return optimizedText;
}
如果发现修剪后会导致最后一个全角字符不能正确显示,则应将那个全角字符整个抛弃。如下图所示,“你好吗”经裁剪后,会变为“你好”,只保留了4个字节的内容。
最后补充说明下:本文中描述的场景中,都是从第0字节的情况,截取指定字节数的内容。其他应用场景需做适当修改,但思路大体一致。
END