在Python中如何将字符串截断为75个字符?

在JavaScript中是这样做的:

var data="saddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddsaddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddsadddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
var info = (data.length > 75) ? data.substring[0,75] + '..' : data;

当前回答

info = data[:75] + ('..' if len(data) > 75 else '')

其他回答

假设string是我们希望截断的字符串,而nchars是输出字符串中所需的字符数。

stryng = "sadddddddddddddddddddddddddddddddddddddddddddddddddd"
nchars = 10

我们可以像下面这样截断字符串:

def truncate(stryng:str, nchars:int):
    return (stryng[:nchars - 6] + " [...]")[:min(len(stryng), nchars)]

某些测试用例的结果如下所示:

s = "sadddddddddddddddddddddddddddddd!"
s = "sa" + 30*"d" + "!"

truncate(s, 2)                ==  sa
truncate(s, 4)                ==  sadd
truncate(s, 10)               ==  sadd [...]
truncate(s, len(s)//2)        ==  sadddddddd [...]

我的解决方案为上面的测试用例产生了合理的结果。

但一些病理病例如下:

一些病理病例!

truncate(s, len(s) - 3)()       ==  sadddddddddddddddddddddd [...]
truncate(s, len(s) - 2)()       ==  saddddddddddddddddddddddd [...]
truncate(s, len(s) - 1)()       ==  sadddddddddddddddddddddddd [...]
truncate(s, len(s) + 0)()       ==  saddddddddddddddddddddddddd [...]
truncate(s, len(s) + 1)()       ==  sadddddddddddddddddddddddddd [...
truncate(s, len(s) + 2)()       ==  saddddddddddddddddddddddddddd [..
truncate(s, len(s) + 3)()       ==  sadddddddddddddddddddddddddddd [.
truncate(s, len(s) + 4)()       ==  saddddddddddddddddddddddddddddd [
truncate(s, len(s) + 5)()       ==  sadddddddddddddddddddddddddddddd 
truncate(s, len(s) + 6)()       ==  sadddddddddddddddddddddddddddddd!
truncate(s, len(s) + 7)()       ==  sadddddddddddddddddddddddddddddd!
truncate(s, 9999)()             ==  sadddddddddddddddddddddddddddddd!

值得注意的是,

当字符串包含换行字符(\n)时,可能会出现问题。 当nchars > len(s)时,我们应该打印字符串s,而不是试图打印“[…]”

下面是更多的代码:

import io

class truncate:
    """
        Example of Code Which Uses truncate:
        ```
            s = "\r<class\n 'builtin_function_or_method'>"
            s = truncate(s, 10)()
            print(s)
                    ```
                Examples of Inputs and Outputs:
                        truncate(s, 2)()   ==  \r
                        truncate(s, 4)()   ==  \r<c
                        truncate(s, 10)()  ==  \r<c [...]
                        truncate(s, 20)()  ==  \r<class\n 'bu [...]
                        truncate(s, 999)() ==  \r<class\n 'builtin_function_or_method'>
                    ```
                Other Notes:
                    Returns a modified copy of string input
                    Does not modify the original string
            """
    def __init__(self, x_stryng: str, x_nchars: int) -> str:
        """
        This initializer mostly exists to sanitize function inputs
        """
        try:
            stryng = repr("".join(str(ch) for ch in x_stryng))[1:-1]
            nchars = int(str(x_nchars))
        except BaseException as exc:
            invalid_stryng =  str(x_stryng)
            invalid_stryng_truncated = repr(type(self)(invalid_stryng, 20)())

            invalid_x_nchars = str(x_nchars)
            invalid_x_nchars_truncated = repr(type(self)(invalid_x_nchars, 20)())

            strm = io.StringIO()
            print("Invalid Function Inputs", file=strm)
            print(type(self).__name__, "(",
                  invalid_stryng_truncated,
                  ", ",
                  invalid_x_nchars_truncated, ")", sep="", file=strm)
            msg = strm.getvalue()

            raise ValueError(msg) from None

        self._stryng = stryng
        self._nchars = nchars

    def __call__(self) -> str:
        stryng = self._stryng
        nchars = self._nchars
        return (stryng[:nchars - 6] + " [...]")[:min(len(stryng), nchars)]

对于Django解决方案(问题中没有提到):

from django.utils.text import Truncator
value = Truncator(value).chars(75)

看看Truncator的源代码来理解这个问题: https://github.com/django/django/blob/master/django/utils/text.py#L66

关于Django的截断: Django HTML截断

如果你想做一些更复杂的字符串截断,你可以采用sklearn方法作为实现:

sklearn.base.BaseEstimator.__repr__ (参见原始完整代码:https://github.com/scikit-learn/scikit-learn/blob/f3f51f9b6/sklearn/base.py#L262)

它增加了一些好处,比如避免在单词中间截断。

def truncate_string(data, N_CHAR_MAX=70):
    # N_CHAR_MAX is the (approximate) maximum number of non-blank
    # characters to render. We pass it as an optional parameter to ease
    # the tests.

    lim = N_CHAR_MAX // 2  # apprx number of chars to keep on both ends
    regex = r"^(\s*\S){%d}" % lim
    # The regex '^(\s*\S){%d}' % n
    # matches from the start of the string until the nth non-blank
    # character:
    # - ^ matches the start of string
    # - (pattern){n} matches n repetitions of pattern
    # - \s*\S matches a non-blank char following zero or more blanks
    left_lim = re.match(regex, data).end()
    right_lim = re.match(regex, data[::-1]).end()
    if "\n" in data[left_lim:-right_lim]:
        # The left side and right side aren't on the same line.
        # To avoid weird cuts, e.g.:
        # categoric...ore',
        # we need to start the right side with an appropriate newline
        # character so that it renders properly as:
        # categoric...
        # handle_unknown='ignore',
        # so we add [^\n]*\n which matches until the next \n
        regex += r"[^\n]*\n"
        right_lim = re.match(regex, data[::-1]).end()
    ellipsis = "..."
    if left_lim + len(ellipsis) < len(data) - right_lim:
        # Only add ellipsis if it results in a shorter repr
        data = data[:left_lim] + "..." + data[-right_lim:]
    return data

你不能像动态分配C字符串那样“截断”Python字符串。Python中的字符串是不可变的。您可以像其他答案中描述的那样对字符串进行切片,生成一个只包含由切片偏移量和步长定义的字符的新字符串。 在某些(不实际的)情况下,这可能有点烦人,比如当你选择Python作为面试语言时,面试官要求你从一个字符串中删除重复的字符。哎。

不需要正则表达式,但您确实希望在接受的答案中使用字符串格式而不是字符串连接。

这可能是将字符串数据截断为75个字符的最规范的python方法。

>>> data = "saddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddsaddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddsadddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
>>> info = "{}..".format(data[:75]) if len(data) > 75 else data
>>> info
'111111111122222222223333333333444444444455555555556666666666777777777788888...'