将字符串转换为字典的简单方法

Posted

技术标签:

【中文标题】将字符串转换为字典的简单方法【英文标题】:Simple way to convert a string to a dictionary 【发布时间】:2010-12-21 07:50:48 【问题描述】:

将关键字=值字符串转换为字典的最简单方法是什么,例如以下字符串:

name="John Smith", age=34, height=173.2, location="US", avatar=":,=)"

到以下python字典:

'name':'John Smith', 'age':34, 'height':173.2, 'location':'US', 'avatar':':,=)'

'avatar' 键只是为了表明字符串可以包含 = 和 ,所以简单的 'split' 是行不通的。有任何想法吗?谢谢!

【问题讨论】:

【参考方案1】:

这对我有用:

# get all the items
matches = re.findall(r'\w+=".+?"', s) + re.findall(r'\w+=[\d.]+',s)

# partition each match at '='
matches = [m.group().split('=', 1) for m in matches]

# use results to make a dict
d = dict(matches)

【讨论】:

这行得通——只需添加例程将最终值转换为字符串/整数等,并可能去掉值中包含的不需要的双引号。 非常好,谢谢!我知道正则表达式会是答案,只是从来没有学会如何有效地使用它们! 相信我的朋友,他们值得付出努力。找一个好的交互式正则表达式测试器(比如 redemo.py),开始动手吧! 请注意,有些字符串会导致上述正则表达式解决方案做一些奇怪的事情,例如avatar="p=0",或更糟的是avatar="age=123"。如果这些让您担心,您将需要一个基于解析器的解决方案。顺便说一句,我不知道您是否可以控制输入格式,但是 JSON 非常接近上面的输入格式,并且几乎每种语言都有模块来解析它。 json.org【参考方案2】:

编辑:由于csv 模块不能按要求处理inside 字段的引号,因此实现此功能需要更多工作:

import re
quoted = re.compile(r'"[^"]*"')

class QuoteSaver(object):

  def __init__(self):
    self.saver = dict()
    self.reverser = dict()

  def preserve(self, mo):
    s = mo.group()
    if s not in self.saver:
      self.saver[s] = '"%d"' % len(self.saver)
      self.reverser[self.saver[s]] = s
    return self.saver[s]

  def expand(self, mo):
    return self.reverser[mo.group()]

x = 'name="John Smith", age=34, height=173.2, location="US", avatar=":,=)"'

qs = QuoteSaver()
y = quoted.sub(qs.preserve, x)
kvs_strings = y.split(',')
kvs_pairs = [kv.split('=') for kv in kvs_strings]
kvs_restored = [(k, quoted.sub(qs.expand, v)) for k, v in kvs_pairs]

def converter(v):
  if v.startswith('"'): return v.strip('"')
  try: return int(v)
  except ValueError: return float(v)

thedict = dict((k.strip(), converter(v)) for k, v in kvs_restored)
for k in thedict:
  print "%-8s %s" % (k, thedict[k])
print thedict

我发送thedict 两次以准确显示它与所需结果的不同之处和原因;输出是:

age      34
location US
name     John Smith
avatar   :,=)
height   173.2
'age': 34, 'location': 'US', 'name': 'John Smith', 'avatar': ':,=)',
 'height': 173.19999999999999

如您所见,当直接使用print 发出时,浮点值的输出是所要求的,但它不是并且 不能 是(因为那里 IS 在这种情况下不会显示173.2 的浮点值!-) 当print 应用于整个dict 时(因为不可避免地在键和值上使用repr - 以及@987654330 @ of 173.2 具有这种形式,考虑到浮点值如何以二进制而不是十进制等存储的常见问题)。你可以定义一个dict 子类,它将__str__ 覆盖为特殊的浮点值,我猜,如果这确实是一个要求的话。

但是,我希望这种分心不会干扰核心思想——只要双引号适当平衡(并且没有双引号内双引号),此代码确实执行保留“特殊字符”(在这种情况下是逗号和等号)在它们位于双引号内时被视为正常意义,即使双引号在 inside 一个“字段”而不是开头字段(csv 只处理后一种情况)。如果代码的工作方式不明显,则插入一些中间打印——首先它将所有“双引号字段”更改为特别简单的形式("0""1" 等),同时分别记录实际内容对应于那些简单的形式是;最后,简单的形式变回原来的内容。双引号剥离(对于字符串)以及将未加引号的字符串转换为整数或浮点数最终由简单的converter 函数处理。

【讨论】:

至于 Managu 的类似解决方案,如果右侧的字符串包含逗号(在我正在使用的情况下它们会这样做),这将不起作用。 你是对的 == csv 不理解字段“中间”的引号。让我想出一些别的东西并修正我的答案。【参考方案3】:

我会建议一种懒惰的方式。

test_string = 'name="John Smith", age=34, height=173.2, location="US", avatar=":,=)"'
eval("dict()".format(test_string))

'age': 34, 'location': 'US', 'avatar': ':,=)', 'name': 'John Smith', 'height': 173.2

希望这对某人有所帮助!

【讨论】:

【参考方案4】:

这是使用 pyparsing 解决问题的更详细的方法。注意解析动作 它自动将类型从字符串转换为整数或浮点数。此外,该 QuotedString 类隐式地从引用的值中去除引号。最后, Dict 类采用逗号分隔列表中的每个 'key = val' 组,并分配 结果名称使用键和值标记。

from pyparsing import *

key = Word(alphas)
EQ = Suppress('=')
real = Regex(r'[+-]?\d+\.\d+').setParseAction(lambda t:float(t[0]))
integer = Regex(r'[+-]?\d+').setParseAction(lambda t:int(t[0]))
qs = QuotedString('"')
value = real | integer | qs

dictstring = Dict(delimitedList(Group(key + EQ + value)))

现在解析您的原始文本字符串,将结果存储在 dd 中。 Pyparsing 返回一个 ParseResults 类型的对象,但此类具有许多类似 dict 的功能(支持 keys(), items()、in 等),或者可以通过调用 asDict() 发出真正的 Python dict。调用转储() 显示原始解析列表中的所有标记,以及所有命名项目。最后 两个示例显示了如何访问 ParseResults 中的命名项目,就好像它们是 一个 Python 对象。

text = 'name="John Smith", age=34, height=173.2, location="US", avatar=":,=)"'
dd = dictstring.parseString(text)
print dd.keys()
print dd.items()
print dd.dump()
print dd.asDict()
print dd.name
print dd.avatar

打印:

['age', 'location', 'name', 'avatar', 'height']
[('age', 34), ('location', 'US'), ('name', 'John Smith'), ('avatar', ':,=)'), ('height', 173.19999999999999)]
[['name', 'John Smith'], ['age', 34], ['height', 173.19999999999999], ['location', 'US'], ['avatar', ':,=)']]
- age: 34
- avatar: :,=)
- height: 173.2
- location: US
- name: John Smith
'age': 34, 'height': 173.19999999999999, 'location': 'US', 'avatar': ':,=)', 'name': 'John Smith'
John Smith
:,=)

【讨论】:

【参考方案5】:

以下代码产生了正确的行为,但有点长!我在头像中添加了一个空格,以表明它可以很好地处理字符串中的逗号和空格以及等号。有什么缩短它的建议吗?

import hashlib

string = 'name="John Smith", age=34, height=173.2, location="US", avatar=":, =)"'

strings = 

def simplify(value):
    try:
        return int(value)
    except:
        return float(value)

while True:
    try:
        p1 = string.index('"')
        p2 = string.index('"',p1+1)
        substring = string[p1+1:p2]
        key = hashlib.md5(substring).hexdigest()
        strings[key] = substring
        string = string[:p1] + key + string[p2+1:]
    except:
        break

d =     
for pair in string.split(', '):
    key, value = pair.split('=')
    if value in strings:
        d[key] = strings[value]
    else:
        d[key] = simplify(value)

print d    

【讨论】:

【参考方案6】:

这是eval 的一种方法,虽然我认为它不可靠,但它适用于您的示例。

>>> import re
>>>
>>> s='name="John Smith", age=34, height=173.2, location="US", avatar=":,=)"'
>>>
>>> eval(""+re.sub('(\w+)=("[^"]+"|[\d.]+)','"\\1":\\2',s)+"")
'age': 34, 'location': 'US', 'name': 'John Smith', 'avatar': ':,=)', 'height': 173.19999999999999
>>>

更新:

最好使用 Chris Lutz 在评论中指出的那个,我相信它更可靠,因为即使 dict 值中有(单/双)引号,它也可能有效。

【讨论】:

如果你打算使用eval,为什么不直接使用eval("dict(" + s + ")")?当 Python 已经支持这种语法时,我们不需要在这里做任何正则表达式替换。【参考方案7】:

这里有一个更强大的正则表达式解决方案:

import re

keyval_re = re.compile(r'''
   \s*                                  # Leading whitespace is ok.
   (?P<key>\w+)\s*=\s*(                 # Search for a key followed by..
       (?P<str>"[^"]*"|\'[^\']*\')|     #   a quoted string; or
       (?P<float>\d+\.\d+)|             #   a float; or
       (?P<int>\d+)                     #   an int.
   )\s*,?\s*                            # Handle comma & trailing whitespace.
   |(?P<garbage>.+)                     # Complain if we get anything else!
   ''', re.VERBOSE)

def handle_keyval(match):
    if match.group('garbage'):
        raise ValueError("Parse error: unable to parse: %r" %
                         match.group('garbage'))
    key = match.group('key')
    if match.group('str') is not None:
        return (key, match.group('str')[1:-1]) # strip quotes
    elif match.group('float') is not None:
        return (key, float(match.group('float')))
    elif match.group('int') is not None:
        return (key, int(match.group('int')))

它会自动将浮点数和整数转换为正确的类型;处理单引号和双引号;处理不同位置的无关空白;如果提供了格式错误的字符串,则会抱怨

>>> s='name="John Smith", age=34, height=173.2, location="US", avatar=":,=)"'
>>> print dict(handle_keyval(m) for m in keyval_re.finditer(s))
'age': 34, 'location': 'US', 'name': 'John Smith', 'avatar': ':,=)', 'height': 173.19999999999999

【讨论】:

【参考方案8】:

一步一步来

d=
mystring='name="John Smith", age=34, height=173.2, location="US", avatar=":,=)"';
s = mystring.split(", ")
for item in s:
    i=item.split("=",1)
    d[i[0]]=i[-1]
print d

【讨论】:

【参考方案9】:

我认为您只需要设置 maxsplit=1,例如以下应该可以工作。

string = 'name="John Smith", age=34, height=173.2, location="US", avatar=":, =)"'
newDict = dict(map( lambda(z): z.split("=",1), string.split(", ") ))

编辑(见评论):

我没有注意到“,”是头像下的值,最好的方法是在生成数据的任何地方转义“,”。更好的是像 JSON ;)。但是,作为 regexp 的替代方法,您可以尝试使用 shlex,我认为它可以生成更清晰的代码。

import shlex

string = 'name="John Smith", age=34, height=173.2, location="US", avatar=":, =)"'
lex = shlex.shlex ( string ) 
lex.whitespace += "," # Default whitespace doesn't include commas
lex.wordchars += "."  # Word char should include . to catch decimal 
words = [ x for x in iter( lex.get_token, '' ) ]
newDict = dict ( zip( words[0::3], words[2::3]) )

【讨论】:

它给我这个'': ')"', 'name': '"John Smith"', 'age': '34', 'height': '173.2', 'location': '"US"', 'avatar': '":'【参考方案10】:

总是用逗号分隔?使用 CSV 模块将行拆分为多个部分(未选中):

import csv
import cStringIO

parts=csv.reader(cStringIO.StringIO(<string to parse>)).next()

【讨论】:

这在右侧的字符串包含逗号的情况下不起作用,例如在上面的“头像”案例中。逗号只有在引号内时才会出现在右侧,所以也许可以考虑到这一点? 如果您使用正确的方言,CSV 应该考虑到这一点。

以上是关于将字符串转换为字典的简单方法的主要内容,如果未能解决你的问题,请参考以下文章

使用python将字符串转换为字典的有用方法

我想将字符串列表转换为字典列表

将字典字符串字符串转换为json字符串[重复]

将带有嵌入括号的字符串转换为字典

如何将字典转换为数组

Python 怎么将列表类字典组字符串转换为列表?