【问题标题】:Inverse of string format in pythonpython中字符串格式的反转
【发布时间】:2018-01-31 07:00:14
【问题描述】:

在python中,我们可以使用str.format来构造这样的字符串:

string_format + value_of_keys = formatted_string 

例如:

FMT = '{name:} {age:} {gender}'                   # string_format
VoK = {'name':'Alice', 'age':10, 'gender':'F'}    # value_of_keys
FoS = FMT.format(**VoK)                           # formatted_string

在这种情况下,formatted_string = 'Alice 10 F'

我只是想知道是否有办法从formatted_stringstring_format 获取value_of_keys?它应该是函数Fun

VoK = Fun('{name:} {age:} {gender}', 'Alice 10 F')
# the value of Vok is expected as {'name':'Alice', 'age':10, 'gender':'F'}

有什么办法可以得到这个功能Fun


添加:

我想说,'{name:} {age:} {gender}''Alice 10 F' 只是一个最简单的例子。实际情况可能更困难,space 分隔符可能不存在。

而且从数学上讲,大部分情况是不可逆的,比如:

FMT = '{key1:}{key2:}'
FoS = 'HelloWorld'

VoK 可以是以下任何一个:

{'key1':'Hello','key2':'World'}
{'key1':'Hell','key2':'oWorld'}
....

所以为了明确这个问题,我想补充两个条件:

1. There are always delimiters between two keys
2. All delimiters are not included in any value_of_keys. 

在这种情况下,这个问题是可以解决的(从数学上讲):)

另一个显示输入和预期输出的示例:

In '{k1:}+{k2:}={k:3}', '1+1=2'    Out {'k1':1,'k2':2, 'k3':3}
In 'Hi, {k1:}, this is {k2:}', 'Hi, Alice, this is Bob' Out {'k1':'Alice', 'k2':'Bob'}

【问题讨论】:

  • 只有在格式化字符串中存在一致的分隔符(如空格)和一定的顺序(键的顺序与值完全一致)时,您才能可靠地提取有用的东西。有这些条件吗?
  • 嗨,问题已编辑@JacobIRR

标签: python regex string format


【解决方案1】:

您确实可以这样做,但使用稍微不同的格式字符串,称为 正则表达式

这是你的做法:

import re
# this is how you write your "format"
regex = r"(?P<name>\w+) (?P<age>\d+) (?P<gender>[MF])"
test_str = "Alice 10 F"
groups = re.match(regex, test_str)

现在您可以使用groups 访问字符串的所有组件:

>>> groups.group('name')
'Alice'
>>> groups.group('age')
'10'
>>> groups.group('gender')
'F'

正则表达式是一个很酷的东西。我建议您在线了解更多信息。

【讨论】:

  • 我编辑了问题以明确定义问题,在您的情况下,解决方案并不像我预期的那样通用,我们不想假设分隔符为 space 并编写手动特定的正则表达式,对吧?
  • @springcc 如果您知道分隔符,只需用该分隔符替换空格即可。如果您不想每次都编写新的正则表达式,则可以使用更通用的正则表达式,例如 (?P&lt;name&gt;\S+) (?P&lt;age&gt;\S+) (?P&lt;gender&gt;\S+),但这显然包含更多内容。
  • 是的,但后一种情况确实是我的初衷...寻找关于字符串格式逆问题的通用解决方案...
【解决方案2】:

我写了一个函数,它似乎工作:

import re

def Fun(fmt,res):

    reg_keys = '{([^{}:]+)[^{}]*}'
    reg_fmts = '{[^{}:]+[^{}]*}'
    pat_keys = re.compile(reg_keys)
    pat_fmts = re.compile(reg_fmts)

    keys = pat_keys.findall(fmt)
    lmts = pat_fmts.split(fmt)
    temp = res
    values = []
    for lmt in lmts:
        if not len(lmt)==0:
            value,temp = temp.split(lmt,1)
            if len(value)>0:
                values.append(value)
    if len(temp)>0:
        values.append(temp)
    return dict(zip(keys,values))

用法:

eg1:

fmt = '{k1:}+{k2:}={k:3}'
res = '1+1=2'
print Fun(fmt,res)
>>>{'k2': '1', 'k1': '1', 'k': '2'}

eg2:

fmt = '{name:} {age:} {gender}'
res = 'Alice 10 F'
print Fun(fmt,res)
>>> 

eg3:

fmt = 'Hi, {k1:}, this is {k2:}'
res = 'Hi, Alice, this is Bob'
print Fun(fmt,res)
>>>{'k2': 'Bob', 'k1': 'Alice'}

【讨论】:

  • 需要将 reg_keys = '{([^{}:]+)[^{}]*}' 更改为 reg_keys = r'{([^{}:]+)[^{}]*}',否则 fmt 中的 '{{' 将失败
  • @Brett7533 嗨,我只是在 python2.7 中测试,它可以在没有 'r' 的情况下工作。但是,它也适用于 'r'
【解决方案3】:

一旦获得新字符串,python 无法确定您是如何创建格式化字符串的。

例如:一旦你的格式 "{something} {otherthing}" 带有带空格的值并且你得到了所需的字符串,你就无法区分带空格的单词是 {something} 还是 {otherthing} 的一部分

但是,如果您知道新字符串的格式并且结果具有一致性,您可能会使用一些技巧。

例如,在您给定的示例中:如果您确定单词后跟空格,然后是数字,然后是空格,然后是单词,那么您可以使用下面的正则表达式来提取值:

>>> import re
>>> my_str = 'Alice 10 F'

>>> re.findall('(\w+)\s(\d+)\s(\w+)', my_str)
[('Alice', '10', 'F')] 

为了从中获得所需的dict,您可以将逻辑更新为:

>>> my_keys = ['name', 'age', 'gender']

>>> dict(zip(my_keys, re.findall('(\w+)\s(\d+)\s(\w+)', my_str)[0]))
{'gender': 'F', 'age': '10', 'name': 'Alice'}

【讨论】:

  • 嗨,我编辑了这个问题,space 并不总是假定存在,这个解决方案可能不像我预期的那样普遍,
  • @springcc 对您的编辑的简单回答是,除非您知道区分两把剑的方法,否则这是不可能的
  • 嗨,有两个条件:1. There are always delimiters between two keys2. All delimiters are not included in any value_of_keys.,这个问题是可以解决的(从数学上来说,很容易证明)。
  • 如果有分隔符,可以使用分隔符来分割字符串。但是,对于您的 '{key1:}{key2:}' 示例 - > 'HelloWorld'。没有办法找回“Hello”和“World”
【解决方案4】:

我建议使用**kwargs 来解决这个问题的另一种方法,例如...

def fun(**kwargs):
    result = '{'
    for key, value in kwargs.iteritems():
        result += '{}:{} '.format(key, value)

    # stripping the last space
    result = result[:-1]
    result += '}'
    return result


print fun(name='Alice', age='10', gender='F')
# outputs : {gender:F age:10 name:Alice}

注意 : kwargs 不是有序的字典,只会将参数的顺序保持到 Python 3.6 版本。如果您需要保持秩序,那么构建一个变通解决方案很容易。

【讨论】:

    【解决方案5】:

    此代码为所有值生成字符串,但它确实将字符串拆分为其组成部分。它取决于分隔符是空格,并且没有包含空格的值。如果任何值包含空格,这将成为一个更难的问题。

    >>> delimiters = ' '
    >>> d = {k: v for k,v in zip(('name', 'age', 'gender'), 'Alice 10 F'.split(delimiters))}
    >>> d
    {'name': 'Alice', 'age': '10', 'gender': 'F'}
    

    【讨论】:

    • 嗨,我认为我们不需要假设存在space 分隔符。我编辑了问题以使其定义明确。
    • @springcc 我刚刚进行了编辑以允许任意字符作为分隔符。
    • 对于这种情况:FMT = '{k1:}+{k2:}={k3}'FoS = '1+1=2' 您的解决方案不起作用:(
    • @springcc 如果你要移动球门柱,没人能回答你的问题。
    • 只是说清楚,这确实是我的初衷。一开始我期望的是字符串格式逆问题的通用解决方案。
    【解决方案6】:

    根据您的要求,我有一个解决方案。 这个解决方案的概念是:

    1. 将所有分隔符更改为相同的分隔符
    2. 用相同的分隔符分割输入字符串
    3. 拿到钥匙
    4. 获取值
    5. 压缩键和值作为字典

    import re
    from collections import OrderedDict
    
    def Func(data, delimiters, delimiter):
        # change all delimiters to delimiter
        for d in delimiters:
            data[0] = data[0].replace(d, delimiter)
            data[1] = data[1].replace(d, delimiter)
    
        # get keys with '{}'
        keys = data[0].split(delimiter)
        # if string starts with delimiter remove first empty element
        if keys[0] == '':
            keys = keys[1:]
    
        # get keys without '{}'
        p = re.compile(r'{([\w\d_]+):*.*}')
        keys = [p.match(x).group(1) for x in keys]
    
        # get values
        vals = data[1].split(delimiter)
        # if string starts with delimiter remove first empty element
        if vals[0] == '':
            vals = vals[1:]
    
        # pack to a dict
        result_1 = dict(zip(keys, vals))
    
        # if you need Ordered Dict
        result_2 = OrderedDict(zip(keys, vals))
    
        return result_1, result_2
    

    用法:

    In_1 = ['{k1}+{k2:}={k3:}', '1+2=3']
    delimiters_1 = ['+', '=']
    result = Func(In_1, delimiters_1, delimiters_1[0])
    # Out_1 = {'k1':1,'k2':2, 'k3':3}
    print(result)
    
    
    In_2 = ['Hi, {k1:}, this is {k2:}', 'Hi, Alice, this is Bob']
    delimiters_2 = ['Hi, ', ', this is ']
    result = Func(In_2, delimiters_2, delimiters_2[0])
    # Out_2 = {'k1':'Alice', 'k2':'Bob'}
    print(result)
    

    输出:

    ({'k3': '3', 'k2': '2', 'k1': '1'}, 
    OrderedDict([('k1', '1'), ('k2', '2'), ('k3', '3')]))
    
    ({'k2': 'Bob', 'k1': 'Alice'}, 
    OrderedDict([('k1', 'Alice'), ('k2', 'Bob')]))
    

    【讨论】:

    • 你好,我贴一个答案,不需要手动写分隔符。
    【解决方案7】:

    试试这个:

    import re
    
    
    def fun():
       k = 'Alice 10 F'
       c = '{name:} {age:} {gender}'
       l = re.sub('[:}{]', '', c)
       d={}
       for i,j in zip(k.split(), l.split()):
           d[j]=i
       print(d)
    

    您可以根据需要更改有趣的参数并将其分配给变量。它接受您要提供的相同字符串。并给出这样的字典:

    {'name': 'Alice', 'age': '10', 'gender': 'F'}
    

    【讨论】:

      【解决方案8】:

      我认为唯一正确的答案是,您要搜索的内容通常是不可能的。你只是没有足够的信息。一个很好的例子是:

      #python 3
      a="12"
      b="34"
      c="56"
      string=f"{a}{b}{c}"
      dic = fun("{a}{b}{c}",string)
      

      现在dic 可能是{"a":"12","b":"34","c":"56"},但也可能只是{"a":"1","b":"2","c":"3456"}。因此,任何通用的反转格式函数最终都不会出现这种歧义。您显然可以在每个变量之间强制使用分隔符,但这会破坏函数的目的。

      我知道这已经在 cmets 中说明了,但也应该添加它作为未来访问者的答案。

      【讨论】:

        猜你喜欢
        • 2012-01-11
        • 1970-01-01
        • 1970-01-01
        • 2019-05-02
        • 2010-10-30
        相关资源
        最近更新 更多