Как я могу преобразовать строку в сокращенную форму?

Альтернативный метод, использующий нарезку индекса, которая оказывается быстрее и масштабируется лучше, чем zip:

def slicezip(a, b):
    result = [0]*(len(a)+len(b))
    result[::2] = a
    result[1::2] = b
    return result

Вы заметите, что это работает, только если len(a) == len(b), но условия для эмуляции zip не будут масштаб с а или б.

Для сравнения:

a = range(100)
b = range(100)

%timeit [j for i in zip(a,b) for j in i]
100000 loops, best of 3: 15.4 µs per loop

%timeit list(chain(*zip(a,b)))
100000 loops, best of 3: 11.9 µs per loop

%timeit slicezip(a,b)
100000 loops, best of 3: 2.76 µs per loop
6
задан Smashery 27 May 2009 в 23:07
поделиться

5 ответов

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

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 ..." - если это не сработает, просто используйте половину символов исходного текста: «Привет ...» - если это подходит, попробуйте посередине между ним и: «Привет, 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;
}
8
ответ дан 8 December 2019 в 16:09
поделиться

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

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

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

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

2
ответ дан 8 December 2019 в 16:09
поделиться
  • Вычислить ширину текста ( в зависимости от шрифта)

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

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

    ellipseWidth = вычислите ширину (...)

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

    что-то вроде: Здравствуйте ...

1
ответ дан 8 December 2019 в 16:09
поделиться

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

/**
 *  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
ответ дан 8 December 2019 в 16:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: