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

时间:2022-11-24 22:04:42

I want to fit strings into a specific width. Example, "Hello world" -> "...world", "Hello...", "He...rld".

Do you know where I can find code for that? It's a neat trick, very useful for representing information, and I'd like to add it in my applications (of course).

Edit: Sorry, I forgot to mention the Font part. Not just for fixed width strings but according to the font face.

我想将字符串放入特定的宽度。例如,“Hello world” - >“...... world”,“Hello ...”,“He ...... rld”。你知道我在哪里可以找到代码吗?这是一个巧妙的技巧,对于表示信息非常有用,我想在我的应用程序中添加它(当然)。编辑:对不起,我忘了提到字体部分。不只是固定宽度的字符串,而是根据字体的面貌。

6 个解决方案

#1


It's a pretty simple algorithm to write yourself if you can't find it anywhere - the pseudocode would be something like:

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

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

or if you want the ellipsis at the start of the string, that second line would be:

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

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

or if you want it in the middle:

或者如果你想在中间:

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

Edit:
I'll assume you're using MFC. Since you're wanting it with fonts, you could use the CDC::GetOutputTextExtent function. Try:

编辑:我假设你正在使用MFC。由于您需要使用字体,因此可以使用CDC :: GetOutputTextExtent函数。尝试:

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

If that's too big, then you can then do a search to try and find the longest string you can fit; and it could be as clever a search as you want - for instance, you could just try "Hello Worl..." and then "Hello Wor..." and then "Hello Wo..."; removing one character until you find it fits. Alternatively, you could do a binary search - try "Hello Worl..." - if that doesn't work, then just use half the characters of the original text: "Hello..." - if that fits, try halfway between it and : "Hello Wo..." until you find the longest that does still fit. Or you could try some estimating heuristic (divide the total length by the desired length, proportionately estimate the required number of characters, and search from there.

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

The simple solution is something like:

简单的解决方案是这样的:

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;
}

#2


It's really pretty trivial; I don't think you'll find specific code unless you have something more structured in mind.

这真的很微不足道;我不认为你会找到具体的代码,除非你有更多的结构。

You basically want:

你基本上想要:

  1. to get the length of the string you have, and the window width.
  2. 获取你拥有的字符串的长度和窗口宽度。

  3. figure out how many charaters you can take from the original string, which will basically be window width-3. Call that k.
  4. 弄清楚你可以从原始字符串中获取多少个字符,它们基本上是窗口宽度-3。叫那个k。

  5. Depending on whether you want to put the ellipsis in the middle or at the right hand end, either take the first floor(k/2) characters from one end, concatenated with "...", then concatenated with the last floor(k/2) characters (with possibly one more character needed because of the division); or take the first k characters, ollowed by "...".
  6. 根据您是要将省略号放在中间还是右侧,要么从一端取第一层(k / 2)字符,与“...”连接,再连接到最后一层(k) / 2)字符(由于划分可能还需要一个字符);或者取第一个k字符,用“......”表示。

#3


I think Smashery's answer is a good start. One way to get to the end result would be to write some test code with some test inputs and desired outputs. Once you have a good set of tests setup, you can implement your string manipulation code until you get all of your tests to pass.

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

#4


  • Calculate the width of the text ( based on the font)
  • 计算文本的宽度(基于字体)

In case you are using MFC the API GetOutputTextExtent will get you the value.

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

  • if the width exceeds the given specific width, calculate the ellipse width first:

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

    ellipseWidth = calculate the width of (...)

    ellipseWidth =计算(...)的宽度

  • Remove the string part with width ellipseWidth from the end and append ellipse.

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

    something like: Hello...

    像:你好......

#5


For those who are interested for a complete routine, this is my answer :

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

/**
 *  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;
  */
}

#6


If you just want the "standard" ellipsis format, ("Hello...") you can use the DrawText (or the equivalient MFC function) function passing DT_END_ELLIPSIS.

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

Check out also DT_WORD_ELLIPSIS (it truncates any word that does not fit in the rectangle and adds ellipses) or DT_PATH_ELLIPSIS (what Explorer does to display long paths).

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

#1


It's a pretty simple algorithm to write yourself if you can't find it anywhere - the pseudocode would be something like:

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

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

or if you want the ellipsis at the start of the string, that second line would be:

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

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

or if you want it in the middle:

或者如果你想在中间:

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

Edit:
I'll assume you're using MFC. Since you're wanting it with fonts, you could use the CDC::GetOutputTextExtent function. Try:

编辑:我假设你正在使用MFC。由于您需要使用字体,因此可以使用CDC :: GetOutputTextExtent函数。尝试:

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

If that's too big, then you can then do a search to try and find the longest string you can fit; and it could be as clever a search as you want - for instance, you could just try "Hello Worl..." and then "Hello Wor..." and then "Hello Wo..."; removing one character until you find it fits. Alternatively, you could do a binary search - try "Hello Worl..." - if that doesn't work, then just use half the characters of the original text: "Hello..." - if that fits, try halfway between it and : "Hello Wo..." until you find the longest that does still fit. Or you could try some estimating heuristic (divide the total length by the desired length, proportionately estimate the required number of characters, and search from there.

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

The simple solution is something like:

简单的解决方案是这样的:

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;
}

#2


It's really pretty trivial; I don't think you'll find specific code unless you have something more structured in mind.

这真的很微不足道;我不认为你会找到具体的代码,除非你有更多的结构。

You basically want:

你基本上想要:

  1. to get the length of the string you have, and the window width.
  2. 获取你拥有的字符串的长度和窗口宽度。

  3. figure out how many charaters you can take from the original string, which will basically be window width-3. Call that k.
  4. 弄清楚你可以从原始字符串中获取多少个字符,它们基本上是窗口宽度-3。叫那个k。

  5. Depending on whether you want to put the ellipsis in the middle or at the right hand end, either take the first floor(k/2) characters from one end, concatenated with "...", then concatenated with the last floor(k/2) characters (with possibly one more character needed because of the division); or take the first k characters, ollowed by "...".
  6. 根据您是要将省略号放在中间还是右侧,要么从一端取第一层(k / 2)字符,与“...”连接,再连接到最后一层(k) / 2)字符(由于划分可能还需要一个字符);或者取第一个k字符,用“......”表示。

#3


I think Smashery's answer is a good start. One way to get to the end result would be to write some test code with some test inputs and desired outputs. Once you have a good set of tests setup, you can implement your string manipulation code until you get all of your tests to pass.

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

#4


  • Calculate the width of the text ( based on the font)
  • 计算文本的宽度(基于字体)

In case you are using MFC the API GetOutputTextExtent will get you the value.

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

  • if the width exceeds the given specific width, calculate the ellipse width first:

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

    ellipseWidth = calculate the width of (...)

    ellipseWidth =计算(...)的宽度

  • Remove the string part with width ellipseWidth from the end and append ellipse.

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

    something like: Hello...

    像:你好......

#5


For those who are interested for a complete routine, this is my answer :

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

/**
 *  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;
  */
}

#6


If you just want the "standard" ellipsis format, ("Hello...") you can use the DrawText (or the equivalient MFC function) function passing DT_END_ELLIPSIS.

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

Check out also DT_WORD_ELLIPSIS (it truncates any word that does not fit in the rectangle and adds ellipses) or DT_PATH_ELLIPSIS (what Explorer does to display long paths).

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