Обрезать строку, не заканчивая в середине слова

Я ищу способ обрезать строку в Python, чтобы не обрезать строку в середине слова.

Например:

Original:          "This is really awesome."
"Dumb" truncate:   "This is real..."
"Smart" truncate:  "This is really..."

Ищу способ сделать "умное" усечение сверху.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
43
0
26 358
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

Ответ принят как подходящий

На самом деле я написал решение для этого в своем недавнем проекте. Я сжал большую часть, чтобы она была немного меньше.

def smart_truncate(content, length=100, suffix='...'):
    if len(content) <= length:
        return content
    else:
        return ' '.join(content[:length+1].split(' ')[0:-1]) + suffix

Что происходит, так это то, что оператор if проверяет, меньше ли ваш контент, чем точка отсечения. Если это не так, он обрезается до желаемой длины, разделяется на пространство, удаляет последний элемент (чтобы вы не обрезали слово), а затем снова объединяет его (при этом добавляя '...') .

Это действительно кратко ... Я бы добавил еще один тест, чтобы избежать пустых строк в случае, если в первых "длинных" символах вообще нет пробелов.

Jonas 22.08.2011 16:36

При усечении нужно было учитывать длину суффикса: return ' '.join(content[:length+1-len(suffix)].split(' ')[0:-1]) + suffix

Stan 20.02.2012 13:36

Здесь есть угловой случай, который может кого-то укусить: если content[:length+1] оканчивается пробелом, возвращаемая строка будет длиннее, чем length. То же самое и с content[:length+1-len(suffix) из комментария @ Stan.

coredumperror 24.04.2018 23:16

@Adam Хороший ответ более 11 лет назад, но при этом такой стойкий. Спасибо, что избавили нас от множества ошибок поиска и кода :-)

blueDroid 25.11.2019 23:41

Это старый, но полезный. Предложил бы, возможно, добавить первую полосу после соединения? ' '.join(content[:length+1].split(' ')[0:-1]).rstrip() + suffix иначе вы можете получить что-то вроде 'hello how are you todiajhsdfaja ...'

Curt 08.12.2020 21:12

Вот немного лучшая версия последней строки в решении Адама:

return content[:length].rsplit(' ', 1)[0]+suffix

(Это немного более эффективно и возвращает более разумный результат в случае, если перед строкой нет пробелов.)

Это интересно по поводу rsplit. Думаю, я никогда с этим не сталкивался.

Adam 30.10.2008 17:53

Быстрый тест двух подходов (Python 2.4.3): Код Адама: >>> smart_truncate ('Быстрая коричневая лиса перепрыгнула через ленивого пса.', 26) «Быстрая коричневая лисица прыгнула ...» С кодом bobince : >>> smart_truncate ('Быстрая коричневая лиса перепрыгнула через ленивого пса.', 26) Быстрая коричневая лиса ...

Patrick Cuff 30.10.2008 18:06

Да, я добавил length + 1 к усечению, чтобы обработать, если усечение естественным образом разбивается точно в конце слова.

Adam 30.10.2008 18:10

Этот лучше. Но я бы поставил его под if и пропустил else, это больше pythonix.

e-satis 01.11.2008 20:31

Что ж, тогда давайте воспользуемся условным выражением: def smart_truncate (content, length = 100, suffix = '...'): return (content if len (content) <= length else content [: length] .rsplit ('' , 1) [0] + суффикс)

hughdbrown 05.03.2009 03:54

Так что будьте уверены, результирующая строка не длиннее, чем длина: return content if len(content) <= length else content[:length-len(suffix)].rsplit(' ', 1)[0] + suffix

kraiz 12.12.2011 11:12
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]. Возможно, вам понадобится дополнительная проверка на случай, когда усечение не требуется.

Brian 30.10.2008 18:36
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

я всегда люблю решения на основе регулярных выражений :)

Corey Goldberg 30.10.2008 18:03

это (по крайней мере, верхнее решение) работает даже для строк без пробелов (тогда оно обрезает границу слова), хотя в этом случае оно не добавляет суффикс :)

Robin Manoli 06.05.2015 09:09

Есть несколько тонкостей, которые могут быть, а могут и не быть для вас проблемами, например, обработка вкладок (например, если вы показываете их как 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
Salami 10.12.2015 19:43

Я только что попробовал этот, и он сломался посреди кластера графемы, поэтому он даже не выполняет правильного разбиения символов, не говоря уже о разбиении слов.

Trejkaz 03.05.2017 02:21

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

Другие вопросы по теме