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

发布于 2024-07-21 17:36:01 字数 230 浏览 8 评论 0原文

我想将字符串适合特定的宽度。 例如,“Hello world”-> “……世界”,“你好……”,“他……rd”。

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

编辑:抱歉,我忘了提及字体部分。 不仅适用于固定宽度的字符串,还根据字体。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

酒中人 2024-07-28 17:36:01

如果您在任何地方都找不到它,那么您可以自己编写一个非常简单的算法 - 伪代码将类似于:

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...”; 删除一个字符,直到找到合适的为止。 或者,您可以进行二分搜索 - 尝试“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;
}

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:

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.

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;
}
三生一梦 2024-07-28 17:36:01

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

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.

暖阳 2024-07-28 17:36:01
  • 计算文本的宽度(
    基于字体)

如果您使用 MFC API GetOutputTextExtent 将为您获取该值。

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

    则计算 (...) 的宽度

  • 将末尾宽度为 ellipseWidth 的字符串部分去掉,追加椭圆。

    类似于:Hello...

  • Calculate the width of the text (
    based on the font)

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

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

    ellipseWidth = calculate the width of (...)

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

    something like: Hello...

不回头走下去 2024-07-28 17:36:01

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

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

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;
  */
}
网白 2024-07-28 17:36:01

这真的很微不足道; 我认为除非您有更结构化的想法,否则您不会找到特定的代码。

您基本上想要:

  1. 获取您拥有的字符串的长度和窗口宽度。
  2. 计算出可以从原始字符串中获取多少个字符,这基本上是窗口宽度-3。 称之为k
  3. 根据您要将省略号放在中间还是右手端,可以从一端取出第一层 (k/2) 个字符,与“...”连接,然后与最后一层 (k/2) 个字符连接/2) 字符(由于除法的原因可能还需要一个字符); 或取前 k 个字符,后跟“...”。

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. figure out how many charaters you can take from the original string, which will basically be window width-3. Call that k.
  3. 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 "...".
眼中杀气 2024-07-28 17:36:01

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

另请查看DT_WORD_ELLIPSIS(它会截断任何不适合矩形的单词并添加省略号)或DT_PATH_ELLIPSIS(资源管理器用于显示长路径的操作)。

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

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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文