如何将字符串转换为缩写形式?

时间:2009-05-27 05:04:57

标签: c++ algorithm mfc string abbreviation

我希望字符串放入特定的宽度。例如,“Hello world” - > “......世界”,“你好......”,“他...... rld”。

你知道我在哪里可以找到代码吗?这是一个巧妙的技巧,对于表示信息非常有用,我想在我的应用程序中添加它(当然)。

编辑:抱歉,我忘了提及字体部分。不只是对于固定宽度的字符串,而是根据字体表面。

6 个答案:

答案 0 :(得分:8)

如果你无法在任何地方找到它,那么写一个非常简单的算法 - 伪代码就像:

if theString.Length > desiredWidth:
    theString = theString.Left(desiredWidth-3) + "...";

或者如果你想要在字符串开头的省略号,那第二行将是:

    theString = "..." + theString.Right(desiredWidth-3);

或者如果你想要它在中间:

    theString = theString.Left((desiredWidth-3)/2) + "..." + theString.Right((desiredWidth-3)/2 + ((desiredWidth-3) mod 2))

修改
我假设你正在使用MFC。由于您需要使用字体,因此可以使用CDC::GetOutputTextExtent功能。尝试:

CString fullString
CSize size = pDC->GetOutputTextExtent(fullString);
bool isTooWide = size.cx > desiredWidth;

如果那个太大了,那么你可以进行搜索,试着找到你能装的最长的字符串;它可以像你想要的那样聪明 - 例如,你可以尝试“Hello Worl ...”然后“Hello Wor ...”然后“Hello Wo ...”;删除一个字符,直到找到它为止。或者,您可以执行binary search - 尝试“Hello Worl ...” - 如果这不起作用,那么只使用原始文本的一半字符:“Hello ...” - 如果适合,在它和它之间尝试:“Hello Wo ...”,直到找到最长的仍然适合。或者您可以尝试一些估计启发式(将总长度除以所需的长度,按比例估计所需的字符数,然后从那里搜索。

简单的解决方案就像:

unsigned int numberOfCharsToUse = fullString.GetLength();
bool isTooWide = true;
CString ellipsis = "...";
while (isTooWide)
{
    numberOfCharsToUse--;
    CString string = fullString.Left(numberOfCharsToUse) + ellipsis;
    CSize size = pDC->GetOutputTextExtent(string);
    isTooWide = size.cx > desiredWidth;
}

答案 1 :(得分:2)

这真的很微不足道;除非你考虑到更具结构性的东西,否则我认为你不会找到具体的代码。

你基本上想要:

  1. 获取您拥有的字符串的长度和窗口宽度。
  2. 弄清楚你可以从原始字符串中获取多少个字符,它们基本上是窗口宽度-3。称之为 k
  3. 根据您是要将省略号放在中间还是右端,取一端(k / 2)字符从一端开始,连接“...”,然后连接到最后一层地板(k / 2)字符(由于划分可能还需要一个字符);或者取第一个k字符,由“......”表示。

答案 2 :(得分:2)

我认为Smashery的答案是一个良好的开端。获得最终结果的一种方法是编写一些带有一些测试输入和所需输出的测试代码。一旦你有一套好的测试设置,你可以实现你的字符串操作代码,直到你通过所有的测试。

答案 3 :(得分:1)

  • 计算文字的宽度( 基于字体)

如果您使用的是MFC,API GetOutputTextExtent会为您提供价值。

  • 如果宽度超过给定的宽度 特定宽度,首先计算椭圆宽度:

    ellipseWidth =计算(...)

  • 的宽度
  • 从末尾删除宽度为ellipseWidth的字符串部分并附加椭圆。

    类似:你好......

答案 4 :(得分:1)

对于那些对完整例程感兴趣的人,这是我的答案

/**
 *  Returns a string abbreviation
 *  example: "hello world" -> "...orld" or "hell..." or "he...rd" or "h...rld"
 *
 *  style:
      0: clip left
      1: clip right
      2: clip middle
      3: pretty middle
 */
CString*
strabbr(
  CDC* pdc,
  const char* s,
  const int area_width,
  int style  )
{
  if (  !pdc || !s || !*s  ) return new CString;

  int len = strlen(s);
  if (  pdc->GetTextExtent(s, len).cx <= area_width  ) return new CString(s);

  int dots_width = pdc->GetTextExtent("...", 3).cx;
  if (  dots_width >= area_width  ) return new CString;

  // My algorithm uses 'left' and 'right' parts of the string, by turns.
  int n = len;
  int m = 1;
  int n_width = 0;
  int m_width = 0;
  int tmpwidth;
  // fromleft indicates where the clip is done so I can 'get' chars from the other part
  bool  fromleft = (style == 3  &&  n % 2 == 0)? false : (style > 0);
  while (  TRUE  ) {
    if (  n_width + m_width + dots_width > area_width  ) break;
    if (  n <= m  ) break; // keep my sanity check (WTF), it should never happen 'cause of the above line

    //  Here are extra 'swap turn' conditions
    if (  style == 3  &&  (!(n & 1))  )
      fromleft = (!fromleft);
    else if (  style < 2  )
      fromleft = (!fromleft); // (1)'disables' turn swapping for styles 0, 1

    if (  fromleft  ) {
      pdc->GetCharWidth(*(s+n-1), *(s+n-1), &tmpwidth);
      n_width += tmpwidth;
      n--;
    }
    else {
      pdc->GetCharWidth(*(s+m-1), *(s+m-1), &tmpwidth);
      m_width += tmpwidth;
      m++;
    }

    fromleft = (!fromleft); // (1)
  }

  if ( fromleft ) m--; else n++;

  // Final steps
  // 1. CString version
  CString*  abbr = new CString;
  abbr->Format("%*.*s...%*.*s", m-1, m-1, s, len-n, len-n, s + n);
  return abbr;

  /* 2. char* version, if you dont want to use CString (efficiency), replace CString with char*,
                       new CString with _strdup("") and use this code for the final steps:

  char* abbr = (char*)malloc(m + (len-n) + 3 +1);
  strncpy(abbr, s, m-1);
  strcpy(abbr + (m-1), "...");
  strncpy(abbr+ (m-1) + 3, s + n, len-n);
  abbr[(m-1) + (len-n) + 3] = 0;
  return abbr;
  */
}

答案 5 :(得分:1)

如果您只想要“标准”省略号格式(“Hello ...”),您可以使用 传递DT_END_ELLIPSIS的{​​{3}}(或等效DrawText)函数。

同时检查DT_WORD_ELLIPSIS(它会截断任何不适合矩形的单词并添加省略号)或DT_PATH_ELLIPSIS(资源管理器用来显示长路径)。