使用python将冒号(:)分隔的字符串解析为对象

时间:2011-04-25 21:20:19

标签: python

我有一个字符串,它是REST API(http://requesttracker.wikia.com/wiki/REST)的返回值,并且使用冒号分隔的键/值对。

id: 123414
name: Peter
message: bla bla
  bla bla

如何将此字符串解析为对象?是否有一个我可以使用的现有python解析器?

这是我要解析的字符串:

'RT/3.8.8 200 Ok\n\nid: ticket/46863\nQueue: customer-test\nOwner: Nobody\nCreator: young.park\nSubject: testing\nStatus: new\nPriority: 0\nInitialPriority: 0\nFinalPriority: 0\nRequestors: superuser@meme.com\nCc:\nAdminCc:\nCreated: Mon Apr 25 15:50:27 2011\nStarts: Not set\nStarted: Not set\nDue: Not set\nResolved: Not set\nTold: Not set\nLastUpdated: Mon Apr 25 15:50:28 2011\nTimeEstimated: 0\nTimeWorked: 0\nTimeLeft: 0\nCF.{Severity}: \n\n'

4 个答案:

答案 0 :(得分:5)

您确实需要说明哪个REST api并提供文档参考。

从表面上看,它看起来并不太难:

# Look Ma, no imports!
>>> s = 'id: 1234\nname: Peter\nmessage: foo bar zot\nmsg2: tee:hee\n'
>>> dict(map(str.strip, line.split(':', 1)) for line in s.splitlines())
{'message': 'foo bar zot', 'msg2': 'tee:hee', 'id': '1234', 'name': 'Peter'}

但是:(1)文档应该指向一个解析器(2)没有什么比一个简单的例子看起来那么容易(参见上面的tee:hee);如果你决定自己滚动,你应该将上面的单行分解为多个步骤,以便你可以做一些错误检查(例如,line.split()正好返回2个。)

在给出api引用之后

更新

乍一看,该网站提供了大量的示例,但没有真正说明格式是什么。我建议你多给一眼;如果失败,请询问作者/维护者。

在给出实际的示例输入后,

更新2 ,并在评论“我刚试了这个并且崩溃了”之后:

提供的代码是对第一个(模糊的)示例输入的响应,其中除了last之外的所有行都包含冒号。它伴随着一个建议,它应该分成几部分,而不是单行,特别是提到检查split(':', 1)的结果。你用了什么代码?究竟什么“崩溃”是什么意思?你有没有尝试自己解决你的问题,并解决它?

您提供了哪些数据?您期待已久的实际样本具有以冒号分隔的键:值行前面带有标题行和空行,后跟空行。通过对单线的微不足道的调整可以忽略这些:

>>> print dict(map(str.strip, line.split(':', 1)) for line in s.splitlines()[2:-1])
{'Status': 'new', 'Resolved': 'Not set', 'CF.{Severity}': '',
'TimeLeft': '0', 'Creator': 'young.park', 'Cc': '', 'Starts': 'Not set',
'Created': 'Mon Apr 25 15:50:27 2011', 'Due': 'Not set',
'LastUpdated': 'Mon Apr 25 15:50:28 2011', 'Started': 'Not set',
'Priority': '0', 'Requestors': 'superuser@meme.com',
'AdminCc': '', 'Owner': 'Nobody', 'Told': 'Not set',
'TimeEstimated': '0', 'InitialPriority': '0', 'FinalPriority': '0',
'TimeWorked': '0', 'Subject': 'testing'}
>>>

注1:手动编辑上面的输出以避免水平滚动。

注2:包括Created和LastUpdated条目( - :,其值包含冒号: - )

如果你不相信幸福地忽略了事情,你可以先做分裂线,并断言第一行包含类似预期标题的东西,并且第二行和最后一行是空的。

答案 1 :(得分:4)

看起来像是YAML。你试过PyYAML吗?

>>> import yaml
>>> s = """id: 123414
... name: Peter
... message: bla bla
...   bla bla"""
>>> yaml.load(s)
{'message': 'bla bla bla bla', 'id': 123414, 'name': 'Peter'}

答案 2 :(得分:0)

鉴于你的问题很糟糕,我们开始想象什么是关键问题,因为我无法相信你从未听说过字符串的方法,所以我认为你不知道在这种情况下如何使用它们。

肯定有一种方法可以通过字符串的方法获得你想要的东西,我对此有了一个想法,但我更喜欢直接转向正则表达式工具,认为难以在冒号后面有第二部分。它

import re

regx = re.compile ('(^[^:]+):((?:[^:]+\r?\n)*[^:]+)$',re.MULTILINE)

coloned = '''id: 123414
name: Peter
message: bla bla
bla bla
the end: of the text'''

print regx.findall(coloned)

给出

[('id', ' 123414'), ('name', ' Peter'), ('message', ' bla bla\nbla bla'), ('the end', ' of the text')]

修改

所以这个“问题”没有任何困难

import re

regx = re.compile ('^([^:\n]+): *(.*?) *$',re.MULTILINE)

ch = ('RT/3.8.8 200 Ok\n'                                    '\n'
      'id: ticket/46863\n'      'Queue: customer-test\n'
      'Owner: Nobo:dy\n'        'Creator: young.park\n'
      'Subject: testing\n'      'Status: new\n'
      'Priority: 0\n'           'InitialPriority: 0\n'
      'FinalPriority: 0\n'      'Requestors: superuser@meme.com\n'
      'Cc:\nAdminCc:\n'         'Created: Mon Apr 25 15:50:27 2011\n'
      'Starts: Not set\n'       'Started: Not set\n'
      'Due: Not set\n'          'Resolved: Not set\n'
      'Told: Not set\n'         'LastUpdated: Mon Apr 25 15:50:28 2011\n'
      'TimeEstimated: 0\n'      'TimeWorked: 0\n'
      'TimeLeft: 0\n'           'CF.{Severity}: \n'           '\n')

print dict(regx.findall(ch))
print

s = 'id: 1234\nname: Peter\nmessage: foo bar zot\nmsg2: tee:hee\n'
print dict(regx.findall(s))

结果

{'Due': 'Not set', 'Priority': '0', 'id': 'ticket/46863', 'Told': 'Not set', 'Status': 'new', 'Started': 'Not set', 'Requestors': 'superuser@meme.com', 'FinalPriority': '0', 'Resolved': 'Not set', 'Created': 'Mon Apr 25 15:50:27 2011', 'AdminCc': '', 'Starts': 'Not set', 'Queue': 'customer-test', 'TimeWorked': '0', 'TimeLeft': '0', 'Creator': 'young.park', 'Cc': '', 'LastUpdated': 'Mon Apr 25 15:50:28 2011', 'CF.{Severity}': '', 'Owner': 'Nobo:dy', 'TimeEstimated': '0', 'InitialPriority': '0', 'Subject': 'testing'}

{'message': 'foo bar zot', 'msg2': 'tee:hee', 'id': '1234', 'name': 'Peter'}

John Machin,我没有弄清楚这个新的正则表达式,我花了一分钟重写,如果我们不必乞求必要的基本信息,一开始就不会花费更多的时间需要回答

三条评论:

  • 如果输入发生变化并且其他地方出现补充空行,则解决方案将崩溃​​,而我的正则表达式解决方案将继续正常运行。您的解决方案需要使用if ':' in line

  • 完成
  • 我比较了执行时间:

    我的正则表达式 0.000152533352703 秒,您的 0.000225727012791 (+ 48%)

添加if ':' in line后,会略长:0.000246958761519秒(+ 62%)

速度在这里并不重要,但在其他应用程序中,最好知道正则表达式非常快(比lxml快100倍,比BeautifulSoup快1000倍)

  • 您是CSV格式的专家。也可以使用StringIO和csv模块的功能解决方案

答案 3 :(得分:0)

Examples看起来像是自定义的http消息(但它们不是;它太简单了);您可以使用rfc822.Message来解析它们:

import rfc822
from cStringIO import StringIO

# skip status line; read headers
m = rfc822.Message(StringIO(raw_text[raw_text.index('\n\n')+2:]))

现在您可以访问各个标题:

>>> m.getheader('queue')
'customer-test'
>>> m.getrawheader('queue')
' customer-test\n'
>>> m.getheader('created')
'Mon Apr 25 15:50:27 2011'
>>> m.getdate('created')
(2011, 4, 25, 15, 50, 27, 0, 1, 0)

所有标题:

>>> from pprint import pprint
>>> pprint(dict(m.items()))
{'admincc': '',
 'cc': '',
 'cf.{severity}': '',
 'created': 'Mon Apr 25 15:50:27 2011',
 'creator': 'young.park',
 'due': 'Not set',
 'finalpriority': '0',
 'id': 'ticket/46863',
 'initialpriority': '0',
 'lastupdated': 'Mon Apr 25 15:50:28 2011',
 'owner': 'Nobody',
 'priority': '0',
 'queue': 'customer-test',
 'requestors': 'superuser@meme.com',
 'resolved': 'Not set',
 'started': 'Not set',
 'starts': 'Not set',
 'status': 'new',
 'subject': 'testing',
 'timeestimated': '0',
 'timeleft': '0',
 'timeworked': '0',
 'told': 'Not set'}