【问题标题】:Variable names and values string to dictionary变量名称和值字符串到字典
【发布时间】:2016-03-24 00:30:00
【问题描述】:

我有一个包含变量名称和值的字符串。名称和值之间没有指定的分隔符,名称可能包含也可能不包含下划线。

string1 = 'Height_A_B132width_top100.0lengthsimple0.00001'

我想将变量放入字典中:

# desired output: dict1 = {'Height_A_B': 132, 'width_top': 100.0, 'lengthsimple': 0.00001}

尝试下面的itertools方法

输入1:

from itertools import groupby
[''.join(g) for _, g in groupby(string1, str.isdigit)]

输出1:

['Height_A_B', '132', 'width_top', '100', '.', '0', 'lengthsimple', '0', '.', '00001']

以下内容应该差不多了,但是 iPython 解释器告诉我这个 str 属性不存在(它在文档中)。总之……

输入2:

[''.join(g) for _, g in groupby(string1, str.isnumeric)]

输出2:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-25-cf931a137f50> in <module>()
----> 1 [''.join(g) for _, g in groupby(string1, str.isnumeric)]

AttributeError: type object 'str' has no attribute 'isnumeric'

无论如何,如果数字包含带有“+”或“-”符号的指数会怎样?

string2 = 'Height_A132width_top100.0lengthsimple1.34e+003'
# desired output: dict2 = {'Height_A_B': 132, 'width_top': 100.0, 'lengthsimple': 1.34e+003}

输入3:

[''.join(g) for _, g in groupby(string2, str.isdigit)]

输出3:

['Height_A', '132', 'width_top', '100', '.', '0', 'lengthsimple', '1', '.', '34', 'e+', '003']

我想知道,是否有人有一个优雅的解决方案?

更新: 下面有一些关于保留数值变量类型(例如 int、float 等)的讨论。事实上,string2 中的科学记数法有点像红鲱鱼,因为如果你创建一个变量

>>> a = 1.34e+003

你得到

>>> print a
1340.0

无论如何,所以产生一个包含 1.34+003 的字符串的机会很低。

所以 string2 是一个更合适的测试用例,如果我们将其更改为,比如说

string2 = 'Height_A132width_top100.0lengthsimple1.34e+99'

【问题讨论】:

  • 我怀疑是否有一个优雅的解决方案,因为输入数据的不优雅。 :)
  • @PM2Ring 哦,亲爱的 :) 真的很痛……

标签: python string python-2.7 dictionary split


【解决方案1】:

这个简单的正则表达式可以工作:

[0-9.+e]+|\D+

创建你的字典:

def pairs(s):
    mtch = re.finditer("[0-9.+e]+|\D+", s)
    m1, m2 = next(mtch, ""), next(mtch, "")
    while m1:
        yield m1.group(), float(m2.group())
        m1, m2 = next(mtch, ""), next(mtch, "")

演示:

In [27]: s =  'Height_A_B132width_top100.0lengthsimple0.00001'

In [28]: print(dict(pairs(s)))
{'Height_A_B': 132.0, 'width_top': 100.0, 'lengthsimple': 1e-05}

In [29]: s = 'Height_A132width_top100.0lengthsimple1.34e+003'

In [30]: print(dict(pairs(s)))
{'width_top': 100.0, 'Height_A': 132.0, 'lengthsimple': 1340.0}

或者对于更通用的方法,您可以使用 ast.literal_eval 来解析值以适用于多种类型:

from ast import literal_eval
def pairs(s):
    mtch = re.finditer("[0-9.+e]+|\D+", s)
    m1, m2 = next(mtch, ""), next(mtch, "")
    while m1:
        yield m1.group(), literal_eval(m2.group())
        m1, m2 = next(mtch, ""), next(mtch, "")

如果您真的关心整数与浮点数:

In [31]: s = 'Height_A132width_top100.0lengthsimple1.34e+99'

In [32]: dict(pairs(s))
Out[32]: {'Height_A': 132, 'lengthsimple': 1.34e+99, 'width_top': 100.0}

【讨论】:

    【解决方案2】:

    您可以使用正则表达式:([^\d.]+)(\d[\d.e+-]*):

    1. [^\d.] 表示:除数字和句点外的所有内容
    2. + 表示一个或多个。
    3. 其他组需要至少一位数字,然后是数字或 e 或 -/+。

    第 1 组是键,第 2 组是值。

    demo

    代码:

    import re
    vals = { x:float(y) if '.' in y else int(y) for (x,y) in (re.findall(r'([^\d.]+)(\d[\d.e+-]*)',string2))} 
    
    {'width_top': 100.0, 'Height_A': 132, 'lengthsimple': 1340.0}
    

    【讨论】:

    • 这适用于 string1,但似乎不适用于 string2,因为最后一个值中的指数。
    • 代码和示例已更新,但仍不完全正确。但您可以扩展正则表达式以仅接受正确的科学数字
    • 现在使用 string2,谢谢!很好的解决方案,但我想知道是否有没有正则表达式魔法的另一种方法?尽管如此,我现在要离开并学习正则表达式:)
    • 虽然,dict中的值是作为字符串存储的。
    • 这真的很棒而且简洁,谢谢,但是声明的期望输出是 dict2 = {'Height_A_B': 132, 'width_top': 100.0, 'lengthsimple': 1.34e+003},其中数值变量保留其类型,例如int、科学记数法等。
    【解决方案3】:

    以科学记数法处理数字有点棘手,但可以使用精心编写的正则表达式。希望我的正则表达式在所有数据上都表现正确。 :)

    import re
    
    def parse_numstr(s):
        ''' Convert a numeric string to a number. 
        Return an integer if the string is a valid representation of an integer,
        Otherwise return a float, if its's a valid rep of a float,
        Otherwise, return the original string '''
        try:
            return int(s)
        except ValueError:
    
            try:
                return float(s)
            except ValueError:
                return s
    
    pat = re.compile(r'([A-Z_]+)([-+]?[0-9.]+(?:e[-+]?[0-9]+)?)', re.I)
    
    def extract(s):
        return dict((k, parse_numstr(v)) for k,v in pat.findall(s))
    
    data = [
        'Height_A_B132width_top100.0lengthsimple0.00001',
        'Height_A132width_top100lengthsimple1.34e+003',
        'test_c4.2E1p-3q+5z123E-2e2.71828',
    ]
    
    for s in data:
        print(extract(s))
    

    输出

    {'Height_A_B': 132, 'width_top': 100.0, 'lengthsimple': 1.0000000000000001e-05}
    {'width_top': 100, 'Height_A': 132, 'lengthsimple': 1340.0}
    {'q': 5, 'p': -3, 'z': 1.23, 'test_c': 42.0, 'e': 2.71828}
    

    请注意,我的正则表达式将接受包含多个小数点的科学记数法格式错误的数字,parse_numstr 将仅作为字符串返回。如果您的数据不包含此类格式错误的数字,那应该不是问题。

    这是一个稍微好一点的正则表达式。它只允许一个小数点,但也会接受小数点两侧没有数字的畸形数字,如@9​​87654325@或.E1等。

    pat = re.compile(r'([A-Z_]+)([-+]?[0-9]*\.?[0-9]*(?:e[-+]?[0-9]+)?)', re.I)
    

    另请参阅this answer,了解以科学记数法捕获数字的正则表达式。

    【讨论】:

    • 你可以return float(s),两者都可以
    • @PadraicCunningham:是的,但是 OP 想要的输出是整数,例如 123,所以我觉得尽可能返回整数是合适的。
    • 也许吧,但我认为他们只是使用这两种类型,因为这是他们的字符串中的内容,但本质上是 132 == 132.0
    • 我个人认为尽可能保留数字类型总是值得的!这样可以避免在比较条件等时产生混淆。感谢您的努力。
    • @feedMe,除非您使用 isinstace 之类的东西,您实际上是在检查类型,否则它不会产生影响。调用 float 也不会改变 1.34e+99 所以不知道你为什么认为这是相关的
    【解决方案4】:

    给你:

    import re
    p = re.compile(ur'([a-zA-z]+)([0-9.]+)')
    test_str = u"Height_A_B132width_top100.0lengthsimple0.00001"
    
    print dict(re.findall(p, test_str))
    

    【讨论】:

    • 谢谢,但是这个解决方案在 string2 上也失败了,因为指数。
    猜你喜欢
    • 1970-01-01
    • 2016-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多