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

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

 

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

 

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

 

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

 

  假设我默认的显示文字为九个,那么在我的平台上显示的就是这样:
  
  
  很明显看到,这两串字符的宽度完全是不同的,而下面的英文字符其实可以容纳更多。所以,以固定数目来做截取的标准,是完全不可取的。
  
  有的朋友或许对GetTextMetrics比较熟悉,想到调用该函数返回字体的平均宽度,然后再与范围宽度相除。但毕竟平均不是实际,有时候用平均值来计算,误差也是很大的。
  
  不过,微软毕竟还是微软,并没有将路给堵绝。它还给我们留了一个GetTextExtentPoint函数,用它来获取字符串所占的空间范围。
  
  在具体说这个函数之前,我们先来看一副图:
  截断过长字符串为省略号算法_第1张图片
  
  这是字符串超出显示范围的一个情况。其中,蓝色方框的区域是显示的区域,绿色方框是应该显示的字符,而红色则是省略号占据的空间。对于我们来说,只需要知道绿色方框能包含多少个字符即可。
  
  而GetTextExtentPoint函数能够计算输入的字符串占据的空间范围,所以通过它进行运算,就能获知我们需要显示多少个字符。现在的问题是,我们如何去调用这个函数?难道先从"你"开始,依次递进,以"你"、"你好"、"你好,"、"你好,这"等等这样的方式一个一个作为形参去进行测试?不用想,这效率,肯定奇差,甚至可能成为拖慢程序的一个禁锢。
  
  所以,简单点,我们就用二分法吧。声明一个函数,它可以接收当前的hdc,显示范围的大小,以及测试的字符串,返回的是该显示范围能容纳下的字符。
  
  故此,函数实现如下:
  
  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; }   
  函数的调用其实也很简单,但我们要注意,调用前先将省略号的空间给去掉。故实际的调用代码简单如下:
  
     //获取省略号的占据范围 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);      
  
  那么,看看我们这函数的实际显示成果吧:
  截断过长字符串为省略号算法_第2张图片

你可能感兴趣的:(算法,windows,测试,平台,微软,2010)