Evaluate python format string for number of values expected

萝らか妹 提交于 2019-12-11 18:34:12

问题


I have a method that receives a format string and a set of values provided by a user, and uses these to write an output to a screen.

def makestring(fmt, vals):
    s = fmt.format(*vals)
    return s

fmt_expecting_three = 'a={:0.2f}, b={:0.4f}, c={:0.1f}'

threevalues = [(x+1)/7. for x in range(3)]

makestring(fmt_expecting_three, threevalues)

produces

'a=0.14, b=0.2857, c=0.4'

I would like to perform a test to discover the number of values matches what the format is "expecting".

I show an ugly test below, that can give incorrect results if you don't set maxcheck high enough. Is there a more natural, less ugly way to find out how many values are expected?

def checkit(fmt, maxcheck=None):
    if maxcheck == None:
        maxcheck = 10
    for i in range(maxcheck-1, 0, -1):
        try:
            fmt.format(*range(i))
        except:
            return i+1

fmt_expecting_three = 'a={:0.2f}, b={:0.4f}, c={:0.1f}'

checkit(fmt_expecting_three)

returns

3

回答1:


I'd do this with string.Formatter. This ensures that the string is actually a valid format string, whilst giving you all the information about the format. Take:

>>> import string
>>> f = string.Formatter()
>>> l = list(f.parse('Hello, {noun!s: ^4} world{}!'))
>>> l
[('Hello, ', 'noun', ' ^4', 's'), (' world', '', '', None), ('!', None, None, None)]

From this you can count the amount by checking if the second item is None or not.

>>> sum(1 for _, field_name, _, _ in l if field_name is not None)
2

And so you can use:

def count_formats(format_string):
    f = string.Formatter()
    formats = f.parse(format_string)
    return sum(1 for _, field_name, _, _ in formats if field_name is not None)

This however doesn't work with nested formats. And so we need to check what can be nested:

>>> list(f.parse('{}'))
[('', '', '', None)]
>>> list(f.parse('{:{}}'))
[('', '', '{}', None)]
>>> list(f.parse('{{}:{}}'))
ValueError: Single '}' encountered in format string
>>> list(f.parse('{!{}:{}}'))
ValueError: Single '}' encountered in format string

And so we only need to check format spec to see if there are any nested formats. And so you can change count_formats to be nested if you'd like:

def count_formats(format_string):
    def nested(s):
        for hit in f.parse(s):
            yield hit
            if hit[2]:
                for nested_hit in nested(hit[2]):
                    yield nested_hit
    f = string.Formatter()
    formats = nested(format_string)
    return sum(1 for _, field_name, _, _ in formats if field_name is not None)



回答2:


Though learning regex is painful, it pays off!

Modified from http://www.rexegg.com/regex-cookbook.html

things = ['pi={:0.2f}, e={:0.4f}', 'pi={:0.2f}, e={q', '{{}',
          '{}wow', 'wow', '{}']      # includes some pathologicals

import re

for thing in things:
    print len(re.findall('{([^{)]*)}', thing)), thing

returns

2 pi={:0.2f}, e={:0.4f}
1 pi={:0.2f}, e={q
1 {{}
1 {}wow
0 wow
1 {}

yay!




回答3:


For something like this, I tend to look at the way the Python interpreter solves the problem, rather than reinventing the wheel.

I found a C function in python 3 called countformat(). (This also looks about the same in Python 2.7.)

Next step would be to see if this function is exposed, or is strictly C. I did a code search. It looks like the answer is no, so I'd probably just copy whatever they did but do it in Python instead. It might be more efficient to write a regular expression, but that C function at least gives you an idea of how to define your regex.

The general algorithm is as follows:

  • Set level to 0 and count to 0.
  • Iterate through the format string. For each character:
  • If you encounter an opening format marker (, [, {:
    • If level is 0, increment count.
    • Increment level.
  • Else if you encounter a closing format marker ), ], }:
    • If level is 0, decrement count.
    • Decrement level.
  • Else if you find some other marker #, &, :, , \t:
    • Do nothing.
  • Else (some other character):
    • If level is 0, increment count.
  • Return count.


来源:https://stackoverflow.com/questions/51609953/evaluate-python-format-string-for-number-of-values-expected

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!