将字符串中的花括号转义为要格式化的次数不确定

时间:2018-07-27 17:08:13

标签: python python-2.7 escaping string-formatting python-2.x

相关

您使用花括号({•••})表示要格式化的字符串部分。如果要使用文字大括号字符以使.format()忽略它们,请使用双大括号({{•••}})。 MCVE:

string = "{format} {{This part won't be formatted. The final string will have literal curly braces here.}}"
print string.format(format='string')

如果您有.format()的链,则每次使用.format()时,括号的数量都会加倍。最后一个被4个花括号包围,并在最终输出中以文字大括号结尾。 MCVE:

string = "{format1} {{format2}} {{{{3rd one won't be formatted. The final string will have literal curly braces here.}}}}"
print string.format(format1='string1').format(format2='string2')

也可以将另一个格式字符串格式化为格式字符串。最后一个被4个花括号包围,并在最终输出中以文字大括号结尾。 MCVE:

string = "{format1} {{{{3rd one won't be formatted. The final string will have literal curly braces here.}}}}"
print string.format(format1='{format2}').format(format2='string')

当根据运行时确定的条件使用.format()链时,会出现问题。如果您希望将一组花括号转义为原义字符,则使用多少个? MCVE:

string = "{} {{{{{{Here I don't know exactly how many curly braces to use because this string is formatted differently due to conditions that I have no control over.}}}}}}"

if fooCondition:
    string = string.format('{} bar')
    if barCondition:
        string = string.format('{} baz')
        if bazCondition:
            string = string.format('{} buzz')
string = string.format('foo')

print string

字符串的第一部分有4个可能的输出:

  1. foo

  2. foo bar

  3. foo baz bar

  4. foo buzz baz bar

根据True的条件,字符串的第二部分以不同数量的花括号结束。我希望第二部分的花括号始终保持逃逸状态,就像不要在每次调用.format()时“掉下一层”。我可以解决这样的问题, MCVE:

string = "{} {{DRY - Don't repeat yourself!}}"

if fooCondition:
    string = string.format('{} bar').replace("{DRY - Don't repeat yourself!}", "{{DRY - Don't repeat yourself!}}")
    if barCondition:
        string = string.format('{} baz').replace("{DRY - Don't repeat yourself!}", "{{DRY - Don't repeat yourself!}}")
        if bazCondition:
            string = string.format('{} buzz').replace("{DRY - Don't repeat yourself!}", "{{DRY - Don't repeat yourself!}}")
string = string.format('foo')

print string

But that's duplicate code (bad practice).

MCVE不是我的真实代码。我的真实代码在Google App Engine网络服务器上运行。超长而复杂。我正在使用字符串中的HTML,CSS和JavaScript。我想通过.format()将内容插入HTML,而不会弄乱CSS和JS的花括号。我当前的实现是不可扩展的,并且很容易出错。我必须管理多达5个连续的花括号(例如:{{{{{•••}}}}})才能通过未触动的.format()链。我需要定期将花括号重新插入未设置固定次数的字符串中。修复此意大利面条式代码的一种优雅方法是什么?

如何在Python格式的字符串中永久转义花括号?

3 个答案:

答案 0 :(得分:2)

一个简单而明显的解决方案是,不要对没有完全控制权的字符串应用.format()。相反,也许这样做

result = thestring.replace('{postURL}', url).replace('{postTitle}', title)

答案 1 :(得分:2)

我放在一起了一个局部格式函数(在python3.x中),该函数重写了字符串格式化方法,以允许您仅格式化需要格式化的字符串部分。编辑:我也包括了python 2版本。

## python 3x version
import string
from _string import formatter_field_name_split
################################################################################
def partialformat(s: str, recursionlimit: int = 10, **kwargs):
    """
    vformat does the acutal work of formatting strings. _vformat is the 
    internal call to vformat and has the ability to alter the recursion 
    limit of how many embedded curly braces to handle. But for some reason 
    vformat does not.  vformat also sets the limit to 2!   

    The 2nd argument of _vformat 'args' allows us to pass in a string which 
    contains an empty curly brace set and ignore them.
    """

    class FormatPlaceholder(object):
        def __init__(self, key):
            self.key = key

        def __format__(self, spec):
            result = self.key
            if spec:
                result += ":" + spec
            return "{" + result + "}"

        def __getitem__(self, item):
            return

    class FormatDict(dict):
        def __missing__(self, key):
            return FormatPlaceholder(key)

    class PartialFormatter(string.Formatter):
        def get_field(self, field_name, args, kwargs):
            try:
                obj, first = super(PartialFormatter, self).get_field(field_name, args, kwargs)
            except (IndexError, KeyError, AttributeError):
                first, rest = formatter_field_name_split(field_name)
                obj = '{' + field_name + '}'

                # loop through the rest of the field_name, doing
                #  getattr or getitem as needed
                for is_attr, i in rest:
                    if is_attr:
                        try:
                            obj = getattr(obj, i)
                        except AttributeError as exc:
                            pass
                    else:
                        obj = obj[i]

            return obj, first

    fmttr = PartialFormatter()
    try:
        fs, _ = fmttr._vformat(s, ("{}",), FormatDict(**kwargs), set(), recursionlimit)
    except Exception as exc:
        raise exc
    return fs

编辑:看起来python 2.x有一些细微的差别。

## python 2.x version
import string
formatter_field_name_split = str._formatter_field_name_split
def partialformat(s, recursionlimit = 10, **kwargs):
    """
    vformat does the acutal work of formatting strings. _vformat is the 
    internal call to vformat and has the ability to alter the recursion 
    limit of how many embedded curly braces to handle. But for some reason 
    vformat does not.  vformat also sets the limit to 2!   

    The 2nd argument of _vformat 'args' allows us to pass in a string which 
    contains an empty curly brace set and ignore them.
    """

    class FormatPlaceholder(object):
        def __init__(self, key):
            self.key = key

        def __format__(self, spec):
            result = self.key
            if spec:
                result += ":" + spec
            return "{" + result + "}"

        def __getitem__(self, item):
            return

    class FormatDict(dict):
        def __missing__(self, key):
            return FormatPlaceholder(key)

    class PartialFormatter(string.Formatter):
        def get_field(self, field_name, args, kwargs):
            try:
                obj, first = super(PartialFormatter, self).get_field(field_name, args, kwargs)
            except (IndexError, KeyError, AttributeError):
                first, rest = formatter_field_name_split(field_name)
                obj = '{' + field_name + '}'

                # loop through the rest of the field_name, doing
                #  getattr or getitem as needed
                for is_attr, i in rest:
                    if is_attr:
                        try:
                            obj = getattr(obj, i)
                        except AttributeError as exc:
                            pass
                    else:
                        obj = obj[i]

            return obj, first

    fmttr = PartialFormatter()
    try:
        fs = fmttr._vformat(s, ("{}",), FormatDict(**kwargs), set(), recursionlimit)
    except Exception as exc:
        raise exc
    return fs

用法:

class ColorObj(object):
    blue = "^BLUE^"
s = '{"a": {"b": {"c": {"d" : {} {foo:<12} & {foo!r} {arg} {color.blue:<10} {color.pink} {blah.atr} }}}}'
print(partialformat(s, foo="Fooolery", arg="ARRrrrrrg!", color=ColorObj))

输出:

{"a": {"b": {"c": {"d" : {} Fooolery             & 'Fooolery' Fooolery ARRrrrrrg! ^BLUE^ {color.pink} {blah.atr} }}}}

答案 2 :(得分:0)

我的解决方案:

我用模糊的Unicode字符替换字符串中的“永久”花括号,我确信我永远不会将其用于任何其他目的。

例如:

  • 我输入“⁍”表示“ {”

  • 我输入“⁌”表示“}”

完成所有格式设置后,我将每个替换字符替换为其对应的花括号。

// Mark: - Set navigation bar title color
func setTitleColorWhite(vc: UIViewController){
    let attrs = [
        NSAttributedStringKey.foregroundColor: UIColor(red:1.00, green:1.00, blue:1.00, alpha:1.0),
        NSAttributedStringKey.font: UIFont(name: "SFProText-Medium", size: 17.0)!
    ]
    vc.navigationController?.navigationBar.titleTextAttributes = attrs
}

我对字符串使用链式和嵌套stringAfterFormatting.replace("⁍", "{").replace("⁌", "}") 的次数无关紧要。

.format()

我格式化了需要格式化的内容,并在末尾string.format(•••).format(•••).format(•••).replace("⁍", "{").replace("⁌", "}") 了花括号。