在str.format中切片字符串

时间:2012-12-28 16:47:03

标签: python string string-formatting

我希望通过str.format实现以下功能:

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

我能做到的唯一方法是:

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

现在,这个例子和我真正拥有的是一个冗长而混乱的字符串,所以我想把切片放在{}里面。我研究过the docs,但我无法弄清楚正确的语法。我的问题是:是否可以在替换字段中切换字符串?

5 个答案:

答案 0 :(得分:15)

不,您不能将切片应用于替换字段内的字符串。

您需要参考Format Specification Mini-Language;它定义了 的可能性。这种迷你语言定义了如何格式化引用值(替换字段语法中:之后的部分)。

答案 1 :(得分:6)

你可以这样做。

注意
这是一个粗略的例子,不应该被认为是完整和测试的。但我认为它会向您展示一种开始到达目的地的方式。

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:]这样的切片,但我认为这也很容易实现。切片索引也没有错误检查超出范围等

答案 2 :(得分:2)

您可以使用运行时评估的“f”字符串。 Python f-strings支持切片,不使用格式化程序之类的“迷你语言”。 python表达式的全部功能在f-string的每个花括号中都可用。不幸的是没有string.feval()函数... imo应该有(语言不应该具有未提供给用户的魔法能力)。

您也无法在字符串类型中添加一个,因为无法修改/扩展内置的python类型。

有关运行时评估f-string的示例,请参阅https://stackoverflow.com/a/49884004/627042

答案 3 :(得分:1)

这是一个很好的解决方案,很好地解决了我的切片问题。但是,我也希望能够实现价值观。例如&#39; AVeryLongStringValue&#39;我可能想要填入10个字符的字段,可能会被截断为&#39; ... ngValue&#39;。所以我扩展了你的例子以支持切片,删除和普通格式化。这就是我想出来的。

  add  $2, $0, $0
  addi $11, $0, 5
  addi $12, $0, 3
outer:
  addi $2, $2, 1
  add  $1, $0, $0
inner:
  addi $1, $1, 1
  bne  $1, $11, inner
  bne  $2, $12, outer

作为示例,可以组合所有三种格式规范来剪切值的第一个和最后一个字符,中心将切片删除为10个char值,最后在12个char字段中右对齐,如下所示:

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

输出:&#39;恩特** VALU&#39 ;.对于其他可能感兴趣的人。非常感谢。

答案 4 :(得分:1)

直接回答您的问题:不,内置的str格式不支持切片。不过,有一种解决方法可以防止f字符串(评估运行时)不符合您的需求。

解决方法

先前扩展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"