将字符串转换或取消格式化为Python中的变量(如format(),但反之)

evs*_*ith 28 python string-formatting

我有形式的字符串Version 1.4.0\nVersion 1.15.6\n,我想从他们身上抽取三个数字的简单方法.我知道我可以使用format方法将变量放入字符串中; 我基本上想要倒退,像这样:

# So I know I can do this:
x, y, z = 1, 4, 0
print 'Version {0}.{1}.{2}\n'.format(x,y,z)
# Output is 'Version 1.4.0\n'

# But I'd like to be able to reverse it:

mystr='Version 1.15.6\n'
a, b, c = mystr.unformat('Version {0}.{1}.{2}\n')

# And have the result that a, b, c = 1, 15, 6
Run Code Online (Sandbox Code Playgroud)

我找到的其他人问了同样的问题,但回复是针对他们的特定情况的: 反向使用Python格式字符串进行解析

一般答案(如何format()反向)会很棒!我的具体案例的答案也会非常有用.

Wil*_*ian 8

>>> import re
>>> re.findall('(\d+)\.(\d+)\.(\d+)', 'Version 1.15.6\n')
[('1', '15', '6')]
Run Code Online (Sandbox Code Playgroud)


Dan*_*anH 8

只是为了建立在Uche的答案上,我一直在寻找一种通过kwargs模式来反转字符串的方法.所以我把以下功能放在一起:

def string_to_dict(string, pattern):
    regex = re.sub(r'{(.+?)}', r'(?P<_\1>.+)', pattern)
    values = list(re.search(regex, string).groups())
    keys = re.findall(r'{(.+?)}', pattern)
    _dict = dict(zip(keys, values))
    return _dict
Run Code Online (Sandbox Code Playgroud)

其工作原理如下:

>>> p = 'hello, my name is {name} and I am a {age} year old {what}'

>>> s = p.format(name='dan', age=33, what='developer')
>>> s
'hello, my name is dan and I am a 33 year old developer'
>>> string_to_dict(s, p)
{'age': '33', 'name': 'dan', 'what': 'developer'}

>>> s = p.format(name='cody', age=18, what='quarterback')
>>> s
'hello, my name is cody and I am a 18 year old quarterback'
>>> string_to_dict(s, p)
{'age': '18', 'name': 'cody', 'what': 'quarterback'}
Run Code Online (Sandbox Code Playgroud)


Ric*_*ica 5

编辑:另请参阅此答案以获取有关parse和的更多信息parmatter

pypi 包parse很好地用于此目的:

pip install parse
Run Code Online (Sandbox Code Playgroud)

可以这样使用:

>>> import parse
>>> result=parse.parse('Version {0}.{1}.{2}\n', 'Version 1.15.6\n')
<Result ('1', '15', '6') {}>
>>> values=list(result)
>>> print(values)
['1', '15', '6']
Run Code Online (Sandbox Code Playgroud)

请注意,文档说parse包默认情况下不会完全模拟格式规范迷你语言;它还使用由re. 特别值得注意的是,s默认情况下这意味着“空白”,而不是str. 通过更改sto的默认类型str(使用extra_types),可以轻松地将其修改为与格式规范一致:

result = parse.parse(format_str, string, extra_types=dict(s=str))
Run Code Online (Sandbox Code Playgroud)

这是string.Formatter使用parse包修改内置类以添加unformat我自己使用的功能的概念性想法:

import parse
from string import Formatter
class Unformatter(Formatter):
    '''A parsable formatter.'''
    def unformat(self, format, string, extra_types=dict(s=str), evaluate_result=True):
        return parse.parse(format, string, extra_types, evaluate_result)
    unformat.__doc__ = parse.Parser.parse.__doc__
Run Code Online (Sandbox Code Playgroud)

重要提示:方法名称parse已被Formatter类使用,因此我选择unformat了避免冲突。

更新:您可以像这样使用它 - 与string.Formatter类非常相似。

格式(与 相同'{:d} {:d}'.format(1, 2)):

>>> formatter = Unformatter() 
>>> s = formatter.format('{:d} {:d}', 1, 2)
>>> s
'1 2' 
Run Code Online (Sandbox Code Playgroud)

取消格式化:

>>> result = formatter.unformat('{:d} {:d}', s)
>>> result
<Result (1, 2) {}>
>>> tuple(result)
(1, 2)
Run Code Online (Sandbox Code Playgroud)

这当然是非常有限的用途,如上所示。但是,我已经提供了一个 pypi 包(parmatter - 一个最初供我自己使用的项目,但也许其他人会发现它有用),它探索了如何将这个想法用于更有用的工作的一些想法。该软件包在很大程度上依赖于上述parse软件包。编辑:在我的带下几年的经验之后,我意识到parmatter(我的第一个包裹!)是一个可怕的、令人尴尬的想法,并已将其删除。


小智 4

实际上,Python 正则表达式库已经提供了您所需的一般功能。您只需稍微更改模式的语法

>>> import re
>>> from operator import itemgetter
>>> mystr='Version 1.15.6\n'
>>> m = re.match('Version (?P<_0>.+)\.(?P<_1>.+)\.(?P<_2>.+)', mystr)
>>> map(itemgetter(1), sorted(m.groupdict().items()))
['1', '15', '6']
Run Code Online (Sandbox Code Playgroud)

如您所见,您必须将(未)格式字符串从 {0} 更改为 (?P<_0>.+)。您甚至可以使用 (?P<_0>\d+) 要求小数。此外,您必须对某些字符进行转义,以防止它们被解释为正则表达式特殊字符。但这又可以再次自动化,例如

>>> re.sub(r'\\{(\d+)\\}', r'(?P<_\1>.+)', re.escape('Version {0}.{1}.{2}'))
'Version\\ (?P<_0>.+)\\.(?P<_1>.+)\\.(?P<_2>.+)'
Run Code Online (Sandbox Code Playgroud)