





На самом деле я написал решение для этого в своем недавнем проекте. Я сжал большую часть, чтобы она была немного меньше.
def smart_truncate(content, length=100, suffix='...'):
if len(content) <= length:
return content
else:
return ' '.join(content[:length+1].split(' ')[0:-1]) + suffix
Что происходит, так это то, что оператор if проверяет, меньше ли ваш контент, чем точка отсечения. Если это не так, он обрезается до желаемой длины, разделяется на пространство, удаляет последний элемент (чтобы вы не обрезали слово), а затем снова объединяет его (при этом добавляя '...') .
При усечении нужно было учитывать длину суффикса: return ' '.join(content[:length+1-len(suffix)].split(' ')[0:-1]) + suffix
Здесь есть угловой случай, который может кого-то укусить: если content[:length+1] оканчивается пробелом, возвращаемая строка будет длиннее, чем length. То же самое и с content[:length+1-len(suffix) из комментария @ Stan.
@Adam Хороший ответ более 11 лет назад, но при этом такой стойкий. Спасибо, что избавили нас от множества ошибок поиска и кода :-)
Это старый, но полезный. Предложил бы, возможно, добавить первую полосу после соединения? ' '.join(content[:length+1].split(' ')[0:-1]).rstrip() + suffix иначе вы можете получить что-то вроде 'hello how are you todiajhsdfaja ...'
Вот немного лучшая версия последней строки в решении Адама:
return content[:length].rsplit(' ', 1)[0]+suffix
(Это немного более эффективно и возвращает более разумный результат в случае, если перед строкой нет пробелов.)
Это интересно по поводу rsplit. Думаю, я никогда с этим не сталкивался.
Быстрый тест двух подходов (Python 2.4.3): Код Адама: >>> smart_truncate ('Быстрая коричневая лиса перепрыгнула через ленивого пса.', 26) «Быстрая коричневая лисица прыгнула ...» С кодом bobince : >>> smart_truncate ('Быстрая коричневая лиса перепрыгнула через ленивого пса.', 26) Быстрая коричневая лиса ...
Да, я добавил length + 1 к усечению, чтобы обработать, если усечение естественным образом разбивается точно в конце слова.
Этот лучше. Но я бы поставил его под if и пропустил else, это больше pythonix.
Что ж, тогда давайте воспользуемся условным выражением: def smart_truncate (content, length = 100, suffix = '...'): return (content if len (content) <= length else content [: length] .rsplit ('' , 1) [0] + суффикс)
Так что будьте уверены, результирующая строка не длиннее, чем длина: return content if len(content) <= length else content[:length-len(suffix)].rsplit(' ', 1)[0] + suffix
def smart_truncate(s, width):
if s[width].isspace():
return s[0:width];
else:
return s[0:width].rsplit(None, 1)[0]
Тестирование:
>>> smart_truncate('The quick brown fox jumped over the lazy dog.', 23) + "..."
'The quick brown fox...'
Примечание. Если width> len (s), вы получите выход за пределы s [width]. Возможно, вам понадобится дополнительная проверка на случай, когда усечение не требуется.
def smart_truncate1(text, max_length=100, suffix='...'):
"""Returns a string of at most `max_length` characters, cutting
only at word-boundaries. If the string was truncated, `suffix`
will be appended.
"""
if len(text) > max_length:
pattern = r'^(.{0,%d}\S)\s.*' % (max_length-len(suffix)-1)
return re.sub(pattern, r'\1' + suffix, text)
else:
return text
ИЛИ ЖЕ
def smart_truncate2(text, min_length=100, suffix='...'):
"""If the `text` is more than `min_length` characters long,
it will be cut at the next word-boundary and `suffix`will
be appended.
"""
pattern = r'^(.{%d,}?\S)\s.*' % (min_length-1)
return re.sub(pattern, r'\1' + suffix, text)
ИЛИ ЖЕ
def smart_truncate3(text, length=100, suffix='...'):
"""Truncates `text`, on a word boundary, as close to
the target length it can come.
"""
slen = len(suffix)
pattern = r'^(.{0,%d}\S)\s+\S+' % (length-slen-1)
if len(text) > length:
match = re.match(pattern, text)
if match:
length0 = match.end(0)
length1 = match.end(1)
if abs(length0+slen-length) < abs(length1+slen-length):
return match.group(0) + suffix
else:
return match.group(1) + suffix
return text
я всегда люблю решения на основе регулярных выражений :)
это (по крайней мере, верхнее решение) работает даже для строк без пробелов (тогда оно обрезает границу слова), хотя в этом случае оно не добавляет суффикс :)
Есть несколько тонкостей, которые могут быть, а могут и не быть для вас проблемами, например, обработка вкладок (например, если вы показываете их как 8 пробелов, но обрабатываете их как 1 символ внутри), обработка различных вариантов взлома и несанкционированного доступа. удаление пробелов или разрешение переноса и т. д. Если что-то из этого желательно, вы можете взглянуть на модуль переноса текста. например:
def truncate(text, max_size):
if len(text) <= max_size:
return text
return textwrap.wrap(text, max_size-3)[0] + "..."
Поведение по умолчанию для слов, больших, чем max_size, состоит в их нарушении (что делает max_size жестким пределом). Вы можете изменить мягкий предел, используемый некоторыми другими решениями здесь, передав break_long_words = False в wrap (), и в этом случае он вернет все слово. Если вы хотите, чтобы это поведение изменило последнюю строку на:
lines = textwrap.wrap(text, max_size-3, break_long_words=False)
return lines[0] + ("..." if len(lines)>1 else "")
Есть несколько других опций, например, expand_tabs, которые могут быть интересны в зависимости от того, какое именно поведение вы хотите.
>>> import textwrap
>>> textwrap.wrap('The quick brown fox jumps over the lazy dog', 12)
['The quick', 'brown fox', 'jumps over', 'the lazy dog']
Вы просто берете первый элемент из этого, и все готово ...
textwrap.shorten("Hello world", width=10, placeholder = "...") будет производить "Hello..."docs.python.org/3.5/library/textwrap.htmlЯ только что попробовал этот, и он сломался посреди кластера графемы, поэтому он даже не выполняет правильного разбиения символов, не говоря уже о разбиении слов.
Из Python 3.4+ вы можете использовать textwrap.shorten. В примере OP:
>>> import textwrap
>>> original = "This is really awesome."
>>> textwrap.shorten(original, width=20, placeholder = "...")
'This is really...'
textwrap.shorten(text, width, **kwargs)
Collapse and truncate the given text to fit in the given width.
First the whitespace in text is collapsed (all whitespace is replaced by single spaces). If the result fits in the width, it is returned. Otherwise, enough words are dropped from the end so that the remaining words plus the placeholder fit within width:
Для Python 3.4+ я бы использовал textwrap.shorten.
Для более старых версий:
def truncate(description, max_len=140, suffix='…'):
description = description.strip()
if len(description) <= max_len:
return description
new_description = ''
for word in description.split(' '):
tmp_description = new_description + word
if len(tmp_description) <= max_len-len(suffix):
new_description = tmp_description + ' '
else:
new_description = new_description.strip() + suffix
break
return new_description
Если вы действительно можете предпочесть усечение целым предложением, а не словом, вот с чего стоит начать:
def smart_truncate_by_sentence(content, length=100, suffix='...',):
if not isinstance(content,str): return content
if len(content) <= length:
return content
else:
sentences=content.split('.')
cs=np.cumsum([len(s) for s in sentences])
n = max(1, len(cs[cs<length]) )
return '.'.join(sentences[:n])+ '. ...'*(n<len(sentences))
Это действительно кратко ... Я бы добавил еще один тест, чтобы избежать пустых строк в случае, если в первых "длинных" символах вообще нет пробелов.