【问题标题】:Escaping curly braces in string to be formatted an undefined number of times将字符串中的花括号转义为未定义的次数
【发布时间】:2018-07-27 17:08:13
【问题描述】:

相关:

您使用花括号 ({•••}) 来表示要格式化的字符串部分。如果您想使用文字大括号字符以便它们被.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()s 链,则每次使用.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()s 链时,就会出现问题。如果您希望将一组花括号作为文字字符进行转义,您使用多少个? 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 格式字符串中永久转义大括号?

【问题讨论】:

  • 您永远不应该使用字符串格式来生成代码。使用适当的模板语言。
  • 您可以在问题中永久提供相关代码。
  • 那你的问题太宽泛了
  • 那我不知道我们能提供什么帮助。
  • 您的输入和预期输出是什么?你如何在字符串上使用format()?如果我们不知道细节,我们无能为力。

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


【解决方案1】:

简单而明显的解决方案是,不要将.format() 应用于您无法完全控制的字符串。相反,也许这样做

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

【讨论】:

    【解决方案2】:

    我组合了一个 partialformat 函数(在 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} }}}}
    

    【讨论】:

      【解决方案3】:

      我的解决方案:

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

      例如:

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

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

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

      stringAfterFormatting.replace("⁍", "{").replace("⁌", "}")
      

      不管我在字符串上使用多少次链接和嵌套.format()s。

      string.format(•••).format(•••).format(•••).replace("⁍", "{").replace("⁌", "}")
      

      我格式化我需要格式化的内容,.replace() 最后的花括号。

      【讨论】:

      • Unicode 专门为此类内容保留了一个私人使用区域。您不必使用稀有字符;你可以使用一个保证不会被公开使用的。 (一些流行的字体使用 PUA 代码点,因此可能会“泄露”,所以在提交之前可以用谷歌搜索你想要使用的字符代码。)
      猜你喜欢
      • 2016-01-13
      • 2010-12-16
      • 2016-03-15
      • 1970-01-01
      • 2019-08-25
      • 1970-01-01
      • 2013-11-21
      • 2023-03-30
      相关资源
      最近更新 更多