Альтернативный метод, использующий нарезку индекса, которая оказывается быстрее и масштабируется лучше, чем 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
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;
}
Это действительно довольно тривиально; Я не думаю, что вы найдете конкретный код, если у вас нет чего-то более структурированного.
В основном вы хотите:
Я думаю, что ответ Smashery - хорошее начало. Один из способов получить конечный результат - написать тестовый код с тестовыми входами и желаемыми выходами. Когда у вас будет хороший набор тестов, вы можете реализовать свой код манипуляции строками, пока не пройдете все тесты.
Если вы используете MFC, API GetOutputTextExtent предоставит вам значение.
, если ширина превышает заданную определенной ширины, сначала вычислите ширину эллипса:
ellipseWidth = вычислите ширину (...)
Удалите часть строки с шириной ellipseWidth с конца и добавьте эллипс.
что-то вроде: Здравствуйте ...
Для тех, кто интересуется полноценной рутиной, это мой ответ :
/**
* 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;
*/
}