之前的方法是获取字符串的Length并判断超过指定长度就从头截取到该长度位置,很明显,如果同时有中英文的情况,就会有问题。
后来在代码中判断了字符串的字节长度,截取长度为20,乘以 字符长度/字节长度 的值(近似计算字符串中英文占比,如果结果比值是1,说明字符长度和字节长度相等,即为全英文;如果小于1,说明字符串中有中文;如果等于0.5,为全中文),得到实际要截取的长度,即全中文截取前10个字符,全英文截取前20个字符,如果是混合的,就是按大概比例截取。以下是部分代码。
int byteLen = System.Text.Encoding.Default.GetBytes(e.Row.Cells[i].Text).Length; int charLen = e.Row.Cells[i].Text.Length; if (byteLen > 20) { int tmpWidth = Convert.ToInt32(Convert.ToDouble(charLen) / Convert.ToDouble(byteLen) * 20); e.Row.Cells[i].Text = e.Row.Cells[i].Text.Substring(0, tmpWidth ) + "..."; }
这个办法能解决大部分情况,但是还是有一些情况会有截取长度不精确导致超过宽度,因为还有符号没有考虑进去,有些符号占的宽度实际上比中文字还长,以及全角字符等等情况。不知道是不是有更好的成熟的办法可以完美解决这个问题?
using System; using System.Collections.Generic; using System.Text; namespace TestChineseString { class Program { #region Members public static string ConstChineseRegex = @"^[\u4e00-\u9fa5]+$"; public static string ConstEnglishRegex = @"^[a-zA-Z_0-9]+$"; public static string ConstChineseEnglishRegex = @"^[\u4e00-\u9fa5\w]+$"; #endregion static void Main(string[] args) { #region int string str = "123我中45有你"; Console.WriteLine(GetLength(str));//13 #endregion Console.ReadKey(); } #region Methods public static int GetLength(string str) { int length = 0; int countChinese = 0; int countEnglish = 0; for (int i = 0; i < str.Length; i++) { char ch = str[i]; if (IsEnglish(ch)) { countEnglish++; } else if (IsChinese(ch)) { countChinese++; } } length = countChinese * 2 + countEnglish; return length; } public static bool IsChinese(string text) { return System.Text.RegularExpressions.Regex.Match(text, ConstChineseRegex, System.Text.RegularExpressions.RegexOptions.Compiled).Success; } public static bool IsChinese(char ch) { return IsChinese(Convert.ToString(ch)); } public static bool IsEnglish(string text) { return System.Text.RegularExpressions.Regex.Match(text, ConstEnglishRegex, System.Text.RegularExpressions.RegexOptions.Compiled).Success; } public static bool IsEnglish(char ch) { return IsEnglish(Convert.ToString(ch)); } public static bool IsChineseEnglish(string text) { return System.Text.RegularExpressions.Regex.Match(text, ConstChineseEnglishRegex, System.Text.RegularExpressions.RegexOptions.Compiled).Success; } public static bool IsChineseEnglish(char ch) { return IsChineseEnglish(Convert.ToString(ch)); } #endregion } }
这个效果好像跟我那个差不太多,也只是区分了中文和英文字符而已,没有办法真正做到判断实际宽度。比如全都是大写W的一串字符串,就跟同样个数其他小写英文字母的字符串长度差很多。如下图,用我上面的方法,英文截15位,中文8位,15个W的英文比8个中文长多了
之前的方法是获取字符串的Length并判断超过指定长度就从头截取到该长度位置,很明显,如果同时有中英文的情况,就会有问题。 ——〉 有啥问题?
看了后面的说明还没明白啥问题?一个中文会比一个英文宽很多啊,大概是两倍多,可能不同字体会有点差距。那如果我的列宽够放20个英文字符,我用length去截取20的话,全英文就没问题。如果是中文呢,截取前20个中文,实际上这20个中文字符占的空间应该是比40个英文字符还宽的,要么就导致换行,要么就导致表格错乱咯
@Bobby_Mei: 实际上你的问题是你无法按照字符的绘制宽度来从源字符串获取完整的子字符串。这个问题跟设备上下文有关,跟你采用的字体有关。解决方式有两种,一种是不检查字符串中字符数,而是在绘制文本时使用 DT_END_ELLIPSIS(这是 GDI 的文字绘制样式,其它图形接口也有对应的方法) 将其交由系统来决定,但是绘制的文本最后会有省略号。另一种方式,先计算字符串中每个字符绘制的实际宽度(在 GDI 中为 DrawText 指定 DT_CALCRECT 参数,其它图形接口也有对应的方法),然后使用总的宽度依次减去每个字符的绘制宽度,直到剩余宽度 <= 0,这样你就得到了实际可以绘制的字符数,然后再用这个字符数去截取字符串。这种方式的好处就是不会在文本最后留下省略号。实际上在任何图形接口中对第一种方式的实现都是按照第二种方式来做的。
@Launcher: 谢谢你的回复,使用图形接口太麻烦了,我已经使用css的text-overflow属性解决,也可以不加省略号。
http://www.w3school.com.cn/tiy/t.asp?f=css3_text-overflow
@Bobby_Mei: 我看到你的提问里的代码用了 Encoding.Default.GetBytes ,我以为你用 string 截取字符串时出现了半个字符,而实际上这是不会发生的,因为 string 使用的是 utf-16,所以我才问你出了什么问题。然后我受你的代码的启发,以为你想只输出必要长度的文字,然后我指出通过字符编码等方式来实现是错误的方向,接着我给出了正确的方式。最后我才明白你是要在浏览器中显示,这个自然就交给样式表来做的,比如在 Winform、WPF 中,都有这样的属性设置,不用开发人员自己使用图形接口来计算。
你这个提问的角度的确是让人兜了不少弯,其实直接就提问在网页中,当文本溢出时,如何不显示超长部分。结果你非得自己先想个解决方案,把简单问题复杂化。
@Launcher: 不好意思,没有表述得太清晰,不过我在标题中已经有写是Gridview这个控件,这个应该是网页才有吧,Winform是DataGridView,不过不仔细看的话可能是会忽略掉。谢谢你的意见,下次会尽量将问题写得清楚一点。
因为基本上没怎么写过Web,所以有点找不到方向,之前在网上找都是找“超长字符串”之类的关键字,没有想到用“文本溢出”去找,所以没找到css这个解决方案。
@Bobby_Mei: 按照大数原则,你使用了 C# 代码,虽然没指明是 Asp.Net,但是框架自带就有 GridView 这个控件,所以一般都会认为就是 Asp.Net 的 GridView,那么按照这个猜测的方向来回答是最有希望猜中你的需求的。但这都是猜测,关键是你自己给出了一个解决方案,让人匪夷所思,以为你使用的不是 Asp.Net GridView 控件,甚至都不是 Asp.Net,可能是个第三方的表格控件,这个控件也可能用于 Winform,WPF 。
所以想的少的人就会按照大数原则去回答你的问题,而想的多的人就会顺着你的解决思路去解决“字符串截取”问题,而不是解决你真正的需求(网页中文本溢出显示问题)。目前回答你的问题的有两人,从回答结果来看,很明显都想多了。
@Launcher: 嗯, 再次感谢你的耐心回复。还好最后解决了。
找到解决方案了,用css的text-overflow属性完美解决。至于在Gridview中的应用,只要把长文本列都转换成模板列,然后把转换后的Label放进一个DIV中,再定义DIV的Class用于文本溢出截断即可。
CSS使用参考:http://www.w3school.com.cn/tiy/t.asp?f=css3_text-overflow
最终实现效果: