44

我正在寻找一种在 Python 中截断字符串的方法,它不会在单词中间截断字符串。

例如:

原文:“这真是太棒了。”
“哑巴”截断:“这是真的……”
“聪明”截断:“这真是……”

我正在寻找一种从上面完成“智能”截断的方法。

4

10 回答 10

66

实际上,我在我最近的一个项目中为此编写了一个解决方案。我已经将其中的大部分压缩到更小一点。

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

发生的情况是 if 语句检查您的内容是否已经小于截止点。如果不是,它会截断到所需的长度,在空格上拆分,删除最后一个元素(这样你就不会截断一个单词),然后将它重新连接在一起(同时添加“...”) .

于 2008-10-30T14:36:03.550 回答
48

这是 Adam 解决方案中最后一行的稍微好一点的版本:

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

(这稍微更有效,并且在字符串前面没有空格的情况下返回更合理的结果。)

于 2008-10-30T14:43:16.343 回答
11

有一些微妙之处可能对您来说是问题,也可能不是问题,例如处理制表符(例如,如果您将它们显示为 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 可能会感兴趣)。

于 2008-10-30T15:47:04.547 回答
9
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
于 2008-10-30T14:59:41.363 回答
9
>>> import textwrap
>>> textwrap.wrap('The quick brown fox jumps over the lazy dog', 12)
['The quick', 'brown fox', 'jumps over', 'the lazy dog']

你只需要第一个元素,你就完成了......

于 2013-12-29T02:54:59.890 回答
3
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...'
于 2008-10-30T14:44:05.170 回答
3

从 Python 3.4+ 开始,您可以使用textwrap.shorten。使用 OP 示例:

>>> import textwrap
>>> original = "This is really awesome."
>>> textwrap.shorten(original, width=20, placeholder="...")
'This is really...'

textwrap.shorten(文本,宽度,**kwargs)

折叠并截断给定的文本以适应给定的宽度。

首先,文本中的空格被折叠(所有空格都被单个空格替换)。如果结果符合宽度,则返回。否则,从末尾删除足够的单词,以便剩余的单词加上占位符适合宽度:

于 2017-11-14T22:24:38.247 回答
0

对于 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
于 2020-11-26T10:40:28.327 回答
0

如果您实际上可能更喜欢按完整句子而不是按单词截断,请从以下内容开始:

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))
于 2021-01-21T22:26:32.093 回答
0

C++ 版本:

string trim(string s, int k) {
    if (s.size()<=k) return s;
    while(k>=0 && s[k]!=' ')
        k--;
    if (k<0) return "";
    string res=s.substr(0, k+1);
    while(res.size() && (res.back()==' '))
        res.pop_back();
    return res;    
}
于 2021-12-28T20:12:57.553 回答