簡體   English   中英

以 str.format 切片字符串

[英]Slicing strings in str.format

我想用str.format實現以下目標:

x,y = 1234,5678
print str(x)[2:] + str(y)[:2]

我能做到的唯一方法是:

print '{0}{1}'.format(str(x)[2:],str(y)[:2])

現在,這是一個例子,我真正擁有的是一個又長又亂的字符串,所以我想把切片放在{}里面。 我研究了文檔,但我無法弄清楚正確的語法。 我的問題是:是否可以在替換字段中分割字符串?

不,您不能對替換字段內的字符串應用切片。

您需要參考Format Specification Mini-Language 它定義了什么可能的。 這種迷你語言定義了如何格式化引用的值(替換字段語法中:之后的部分)。

你可以做這樣的事情。

筆記
這是一個粗略的例子,不應該被認為是完整的和經過測試的。 但我認為它向你展示了一種開始到達你想去的地方的方法。

import string

class SliceFormatter(string.Formatter):

    def get_value(self, key, args, kwds):
        if '|' in key:
            try:
                key, indexes = key.split('|')
                indexes = map(int, indexes.split(','))
                if key.isdigit():
                    return args[int(key)][slice(*indexes)]
                return kwds[key][slice(*indexes)]
            except KeyError:
                return kwds.get(key, 'Missing')
        return super(SliceFormatter, self).get_value(key, args, kwds)


phrase = "Hello {name|0,5}, nice to meet you.  I am {name|6,9}.  That is {0|0,4}."
fmt = SliceFormatter()
print fmt.format(phrase, "JeffJeffJeff", name="Larry Bob")

輸出

Hello Larry, nice to meet you.  I am Bob.  That is Jeff.

筆記2
不支持像[:5][6:]這樣的切片,但我認為這也很容易實現。 此外,對於超出范圍的切片索引等也沒有錯誤檢查。

您可以使用運行時評估的“f”字符串。 Python f-strings 支持切片,並且不使用格式化程序之類的“迷你語言”。 在 f 字符串的每個花括號中都可以使用 python 表達式的全部功能。 不幸的是,沒有 string.feval() 函數...... imo 應該有(語言不應該有沒有提供給用戶的魔法能力)。

您也不能在字符串類型中添加一個,因為無法修改/擴展內置的 python 類型。

有關運行時評估 f 字符串的示例,請參閱https://stackoverflow.com/a/49884004/627042

直接回答您的問題:不,內置 str 格式不支持切片。 雖然,如果 f-strings(運行時評估)不符合您的需求,有一種解決方法。

解決方法

之前擴展string.Formatter的答案並不完全正確,因為重載 get_value 不是將切片機制添加到string.Formatter的正確方法。

import string


def transform_to_slice_index(val: str):
    if val == "_":
        return None
    else:
        return int(val)


class SliceFormatter(string.Formatter):

    def get_field(self, field_name, args, kwargs):
        slice_operator = None
        if type(field_name) == str and '|' in field_name:
            field_name, slice_indexes = field_name.split('|')
            slice_indexes = map(transform_to_slice_index,
                                slice_indexes.split(','))
            slice_operator = slice(*slice_indexes)

        obj, first = super().get_field(field_name, args, kwargs)
        if slice_operator is not None:
            obj = obj[slice_operator]

        return obj, first

解釋

get_valueget_field內部調用,它僅用於從vformat()訪問 args 和 kwargs。 attr 和項目訪問在 get_field 中完成。 因此,切片訪問應該在 super().get_field 返回所需的 obj 之后完成。

話雖如此,重載 get_value 會給您帶來一個問題,即在遍歷對象后格式化程序將無法用於切片。 您可以在此示例中看到錯誤:

WrongSliceFormatter().format("{foo.bar[0]|1,3}", foo=foo)
>> ValueError: "Only '.' or '[' may follow ']' in format field specifier"

這是一個很好的解決方案,很好地解決了我的切片問題。 然而,我也想做價值消除。 例如,我可能想要填充 10 個字符字段的“AveryLongStringValue”可能會被截斷為“...ngValue”。 因此,我擴展了您的示例以支持切片、刪除和正常格式合二為一。 這就是我想出的。

class SliceElideFormatter(string.Formatter):
    """An extended string formatter that provides key specifiers that allow
    string values to be sliced and elided if they exceed a length limit.  The
    additional formats are optional and can be combined with normal python
    formatting.  So the whole syntax looks like:
    key[|slice-options][$elide-options[:normal-options]
    Where slice options consist of '|' character to begin a slice request,
    followed by slice indexes separated by commas.  Thus {FOO|5,} requests
    everything after the 5th element.
      The elide consist of '$' character followed by an inter max field value,
    followed by '<', '^', or '>' for pre, centered, or post eliding, followed
    by the eliding string.  Thus {FOO$10<-} would display the last 9 chanacters
    of a string longer then 10 characters with '-' prefix.
      Slicing and eliding can be combined.  For example given a dict of
    {'FOO': 'centeredtextvalue', and a format string of 
    '{FOO|1,-1$11^%2E%2E%2E}' would yield 'ente...valu'.  The slice spec removes
    the first and last characrers, and the elide spec center elides the
    remaining value with '...'.  The '...' value must be encoded in URL format
    since . is an existing special format character.
    """

    def get_value(self, key, args, kwds):
        """Called by string.Formatter for each format key found in the format
        string.  The key is checked for the presence of a slice or elide intro-
        ducer character.  If one or both a found the slice and/or elide spec
        is extracted, parsed and processed on value of found with the remaining
        key string.
        Arguments:
          key, A format key string possibly containing slice or elide specs
          args, Format values list tuple
          kwds, Format values key word dictrionary
        """
        sspec = espec = None
        if '|' in key:
            key, sspec = key.split('|')
            if '$' in sspec:
                sspec, espec = sspec.split('$')
        elif '$' in key:
            key, espec = key.split('$')
        value = args[int(key)] if key.isdigit() else kwds[key]
        if sspec:
            sindices = [int(sdx) if sdx else None
                        for sdx in sspec.split(',')]
            value = value[slice(*sindices)]
        if espec:
            espec = urllib.unquote(espec)
            if '<' in espec:
                value = self._prefix_elide_value(espec, value)
            elif '>' in espec:
                value = self._postfix_elide_value(espec, value)
            elif '^' in espec:
                value = self._center_elide_value(espec, value)
            else:
                raise ValueError('invalid eliding option %r' % elidespec)
        if sspec or espec:
            return value

        return super(SliceElideFormatter,self).get_value(key, args, kwds)

    def _center_elide_value(self, elidespec, value):
        """Return center elide value if it exceeds the elide length.
        Arguments:
          elidespec, The elide spec field extracted from key
          value, Value obtained from remaing key to maybe be elided
        """
        elidelen, elidetxt = elidespec.split('^')
        elen, vlen = int(elidelen), len(value)
        if vlen > elen:
            tlen = len(elidetxt)
            return value[:(elen-tlen)//2] + elidetxt + value[-(elen-tlen)//2:]
        return value

    def _postfix_elide_value(self, elidespec, value):
        """Return postfix elided value if it exceeds the elide length.
        Arguments:
          elidespec, The elide spec field extracted from key
          value, Value obtained from remaing key to maybe be elided
        """
        elidelen, elidetxt = elidespec.split('>')
        elen, vlen  = int(elidelen), len(value)
        if vlen > elen:
            tlen = len(elidetxt)
            return value[:(elen-tlen)] + elidetxt
        return value

    def _prefix_elide_value(self, elidespec, value):
        """Return prefix elided value if it exceeds the elide length.
        Arguments:
          elidespec, The elide spec field extracted from key
          value, Value obtained from remaing key to maybe be elided
        """
        elidelen, elidetxt = elidespec.split('<')
        elen, vlen  = int(elidelen), len(value)
        if vlen > elen:
            tlen = len(elidetxt)
            return elidetxt + value[-(elen-tlen):]
        return value

例如,可以組合所有三種格式規范以剪切值的第一個和最后一個字符,將切片居中刪除為 10 字符值,最后在 12 字符字段中右對齊,如下所示:

sefmtr = SliceElideFormatter()
data = { 'CNT':'centeredtextvalue' }
fmt = '{CNT|1,-1$10^**:>12}'
print '%r' % sefmtr.format(fmt, *(), **data)

輸出:'ente**value'。 對於其他可能感興趣的人。 非常感謝。

我試過在 python 3.9 中做它,它運行良好

 x="nowpossible"
print(" slicing is possible {}".format(x[0:2]))

輸出

slicing is possible now

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM