Обрезать строку без конца в середине слова - PullRequest
41 голосов
/ 30 октября 2008

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

Например:

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

Я ищу способ выполнить "умное" усечение сверху.

Ответы [ 7 ]

60 голосов
/ 30 октября 2008

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

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

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

44 голосов
/ 30 октября 2008

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

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

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

11 голосов
/ 30 октября 2008

Есть несколько тонкостей, которые могут или не могут быть проблемой для вас, например, обработка вкладок (например, если вы отображаете их как 8 пробелов, но рассматриваете их как 1 символ внутри), обрабатывает различные варианты разбивания и неразрывный пробел, или разрешение разбивать на переносы и т. д. Если что-то из этого желательно, вы можете взглянуть на модуль textwrap. например:

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, которые могут представлять интерес в зависимости от того, какое именно поведение вы хотите.

7 голосов
/ 30 октября 2008
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

OR

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)

OR

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
6 голосов
/ 29 декабря 2013
>>> import textwrap
>>> textwrap.wrap('The quick brown fox jumps over the lazy dog', 12)
['The quick', 'brown fox', 'jumps over', 'the lazy dog']

Вы просто берете первый элемент этого и все готово ...

3 голосов
/ 30 октября 2008
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...'
1 голос
/ 15 ноября 2017

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

Свернуть и обрезать заданный текст, чтобы уместить в заданную ширину.

Сначала пропускаются пробелы в тексте (все пробелы заменяются одиночными пробелами). Если результат вписывается в ширину, это вернулся. В противном случае достаточно слов отбрасывается с конца, чтобы Остальные слова плюс заполнитель вписываются в ширину:

...