截断过长字符串为省略号算法

//=====================================================================
//TITLE:
//    截断过长字符串为省略号算法
//AUTHOR:
//    norains
//DATE:
//    Wednesday 02-June-2010
//Environment:
//    Windows CE 5.0
//=====================================================================

    真的很想说,这标题咋看咋别扭,咋念咋拗口,但我实在想不出更好的标题了,就先权当如此吧。如果你有更好的想法,欢迎告诉我,谢谢!:)

    为避免浪费各位看官的时间,我们还是直接转入正题吧。在实际的开发当中,我们经常会遇到要字符串比显示的区域要长的情况,从而导致显示不全。最合适的方法自然是跑马灯的滚动显示,不过这个可能稍微复杂了点,并不是所有的情况下都能适用;也许最最简易的,可能就是直接将超出范围的字符串用省略号替代了。

    好像不是很难,不是么?但实际并非如此。比如,有一个字符串"你好,我们可以聊聊么?",你是决定只显示哪部分,而哪部分是被替代的呢?这个字符串所要显示的文字,如何确定?大部分菜鸟的第一反应,估计就是固定要只是显示多少个字符,超过的,就一律以省略号替代。实际上,这是不行的。如果字符全部为中文,或是全部为英文还行,如果又有中文和英文,那么由于互相字符宽度不同,显示就很死翘翘了。

  假设我默认的显示文字为九个,那么在我的平台上显示的就是这样:
  
  
  很明显看到,这两串字符的宽度完全是不同的,而下面的英文字符其实可以容纳更多。所以,以固定数目来做截取的标准,是完全不可取的。
  
  有的朋友或许对GetTextMetrics比较熟悉,想到调用该函数返回字体的平均宽度,然后再与范围宽度相除。但毕竟平均不是实际,有时候用平均值来计算,误差也是很大的。
  
  不过,微软毕竟还是微软,并没有将路给堵绝。它还给我们留了一个GetTextExtentPoint函数,用它来获取字符串所占的空间范围。
  
  在具体说这个函数之前,我们先来看一副图:
  
  
  这是字符串超出显示范围的一个情况。其中,蓝色方框的区域是显示的区域,绿色方框是应该显示的字符,而红色则是省略号占据的空间。对于我们来说,只需要知道绿色方框能包含多少个字符即可。
  
  而GetTextExtentPoint函数能够计算输入的字符串占据的空间范围,所以通过它进行运算,就能获知我们需要显示多少个字符。现在的问题是,我们如何去调用这个函数?难道先从"你"开始,依次递进,以"你"、"你好"、"你好,"、"你好,这"等等这样的方式一个一个作为形参去进行测试?不用想,这效率,肯定奇差,甚至可能成为拖慢程序的一个禁锢。
  
  所以,简单点,我们就用二分法吧。声明一个函数,它可以接收当前的hdc,显示范围的大小,以及测试的字符串,返回的是该显示范围能容纳下的字符。
  
  故此,函数实现如下:
  
  view plaincopy to clipboardprint?
DWORD GetComfortSize(HDC hdc,DWORD dwWidth,const TSTRING &strText)  
{  
 //二分法查找  
 
 DWORD dwComfortSize = 0;  
 DWORD dwBeginSize = 0;  
 DWORD dwEndSize = strText.size();  
   
 while(TRUE)  
 {  
  DWORD dwMiddleSize = (dwEndSize + dwBeginSize) / 2;  
  if(dwMiddleSize == dwBeginSize || dwMiddleSize == dwEndSize)  
  {  
   //两个点之间已经没有数值可以检测,退出循环  
   dwComfortSize = dwBeginSize;  
   break;  
  }  
 
  SIZE sizeChk = {0};  
  ::GetTextExtentPoint(hdc,strText.c_str(),dwMiddleSize,&sizeChk);  
 
  if(sizeChk.cx == dwWidth)  
  {  
   //数值刚好合适,跳出循环  
   dwComfortSize = dwMiddleSize;  
   break;  
  }  
  else if(static_cast<DWORD>(sizeChk.cx) > dwWidth)  
  {  
   //重新设置边界  
   dwEndSize = dwMiddleSize;  
  }  
  else 
  {  
   //重新设置边界  
   dwBeginSize = dwMiddleSize;  
  }  
 
 }  
 
 return dwComfortSize;  
     }  
   
DWORD GetComfortSize(HDC hdc,DWORD dwWidth,const TSTRING &strText)
{
 //二分法查找

 DWORD dwComfortSize = 0;
 DWORD dwBeginSize = 0;
 DWORD dwEndSize = strText.size();
 
 while(TRUE)
 {
  DWORD dwMiddleSize = (dwEndSize + dwBeginSize) / 2;
  if(dwMiddleSize == dwBeginSize || dwMiddleSize == dwEndSize)
  {
   //两个点之间已经没有数值可以检测,退出循环
   dwComfortSize = dwBeginSize;
   break;
  }

  SIZE sizeChk = {0};
  ::GetTextExtentPoint(hdc,strText.c_str(),dwMiddleSize,&sizeChk);

  if(sizeChk.cx == dwWidth)
  {
   //数值刚好合适,跳出循环
   dwComfortSize = dwMiddleSize;
   break;
  }
  else if(static_cast<DWORD>(sizeChk.cx) > dwWidth)
  {
   //重新设置边界
   dwEndSize = dwMiddleSize;
  }
  else
  {
   //重新设置边界
   dwBeginSize = dwMiddleSize;
  }

 }

 return dwComfortSize;
     }
  
 
  函数的调用其实也很简单,但我们要注意,调用前先将省略号的空间给去掉。故实际的调用代码简单如下:
  
     view plaincopy to clipboardprint?
//获取省略号的占据范围  
     static const TSTRING FLAG_ELLIPSIS = TEXT("...");  
 SIZE sizeEllipsis = {0};  
     GetTextExtentPoint(hdc,FLAG_ELLIPSIS.c_str(),FLAG_ELLIPSIS.size(),&sizeEllipsis);  
    
     DWORD dwMaxDisp = GetComfortSize(hdc,sizeExtent.cx - sizeEllipsis.cx,strChk); 
//获取省略号的占据范围
     static const TSTRING FLAG_ELLIPSIS = TEXT("...");
 SIZE sizeEllipsis = {0};
     GetTextExtentPoint(hdc,FLAG_ELLIPSIS.c_str(),FLAG_ELLIPSIS.size(),&sizeEllipsis);
  
     DWORD dwMaxDisp = GetComfortSize(hdc,sizeExtent.cx - sizeEllipsis.cx,strChk);
     
  
  那么,看看我们这函数的实际显示成果吧:
  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值