Как я могу преобразовать строку в сокращенную форму? - PullRequest
6 голосов
/ 27 мая 2009

Я хочу, чтобы поместил строки в определенную ширину. Пример: "Hello world" -> "... world", "Hello ...", "He ... rld".

Вы знаете, где я могу найти код для этого? Это хитрый трюк, очень полезный для представления информации, и я хотел бы добавить его в свои приложения (конечно).

Редактировать : Извините, я забыл упомянуть часть Шрифт . Не только для строк фиксированной ширины, но в соответствии с шрифтом.

Ответы [ 6 ]

8 голосов
/ 27 мая 2009

Это довольно простой алгоритм, который можно написать самому, если вы нигде его не найдете - псевдокод будет выглядеть примерно так:

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

Edit:
Я предполагаю, что вы используете MFC. Поскольку вы хотите использовать шрифты, вы можете использовать функцию CDC :: GetOutputTextExtent . Попробуйте:

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

Если он слишком большой, тогда вы можете выполнить поиск, чтобы попытаться найти самую длинную строку, которая вам подходит; и это может быть настолько умный поиск, насколько вы хотите - например, вы можете просто попробовать «Hello Worl ...», а затем «Hello Wor ...» и затем «Hello Wo ...»; удаление одного символа, пока вы не найдете его подходит. В качестве альтернативы, вы можете выполнить бинарный поиск - попробуйте "Hello Worl ..." - если это не сработает, тогда просто используйте половину символов исходного текста: "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;
}
2 голосов
/ 27 мая 2009

Я думаю, что ответ Smashery - хорошее начало. Одним из способов достижения конечного результата является написание некоторого тестового кода с некоторыми тестовыми входами и желаемыми выходами. После того, как вы настроите хороший набор тестов, вы можете реализовать свой код манипуляции со строками, пока не пройдете все свои тесты.

2 голосов
/ 27 мая 2009

Это действительно довольно тривиально; Я не думаю, что вы найдете конкретный код, если у вас нет чего-то более структурированного.

Вы в основном хотите:

  1. чтобы получить длину строки и ширину окна.
  2. выясните, сколько символов вы можете взять из исходной строки, которая в основном будет шириной окна-3. Назовите это k .
  3. В зависимости от того, хотите ли вы поместить многоточие в середину или в правый конец, либо возьмите символы первого этажа (k / 2) с одного конца, объединенные с "...", затем объединенные с последним символы этажа (k / 2) (возможно, из-за разделения требуется еще один символ); или возьмите первые k символов, обозначенных "...".
1 голос
/ 31 марта 2017

Если вы просто хотите использовать «стандартный» формат многоточия («Hello ...»), вы можете использовать DrawText (или эквивалентная функция MFC ), передающая DT_END_ELLIPSIS.

Проверьте также DT_WORD_ELLIPSIS (оно усекает любое слово, которое не помещается в прямоугольник и добавляет эллипсы) или DT_PATH_ELLIPSIS (что делает Проводник для отображения длинных путей).

1 голос
/ 28 мая 2009

Для тех, кто интересуется полной рутиной, это мой ответ :

/**
 *  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;
  */
}
1 голос
/ 27 мая 2009
  • Рассчитать ширину текста ( на основе шрифта)

В случае, если вы используете MFC, API GetOutputTextExtent даст вам значение.

  • если ширина превышает заданную определенная ширина, сначала вычислите ширину эллипса:

    ellipseWidth = рассчитать ширину (...)

  • Удалить часть строки с шириной ellipseWidth от конца и добавить эллипс.

    что-то вроде: Hello ...

...