使用Pyparsing解析带随机引号的CSV字符串

6 投票
2 回答
793 浏览
提问于 2025-04-15 22:31

我有一个像下面这样的字符串:

<118>date=2010-05-09,time=16:41:27,device_id=FE-2KA3F09000049,log_id=0400147717,log_part=00,type=statistics,subtype=n/a,pri=information,session_id=o49CedRc021772,from="prvs=4745cd07e1=example@example.org",mailer="mta",client_name="example.org,[194.177.17.24]",resolved=OK,to="example@example.org",direction="in",message_length=6832079,virus="",disposition="Accept",classifier="Not,Spam",subject="=?windows-1255?B?Rlc6IEZ3OiDg5fDp5fog+fno5fog7Pf46eHp7S3u4+Tp7SE=?="

我尝试使用CSV模块,但不太合适,因为我找不到忽略引号内容的方法。 Pyparsing看起来是个更好的选择,但我还没找到声明所有语法的方法。

目前,我在用我以前的Perl脚本来解析它,但我想用Python来实现。 如果你需要我的Perl代码,我很乐意提供。

任何帮助都非常感谢。

2 个回答

6

使用现成的解析器可能比自己随便写正则表达式要好。

parse_http_list(s)
    Parse lists as described by RFC 2068 Section 2.

    In particular, parse comma-separated lists where the elements of
    the list may include quoted-strings.  A quoted-string could
    contain a comma.  A non-quoted string could have quotes in the
    middle.  Neither commas nor quotes count if they are escaped.
    Only double-quotes count, not single-quotes.

parse_keqv_list(l)
    Parse list of key=value strings where keys are not duplicated.

举个例子:

>>> pprint.pprint(urllib2.parse_keqv_list(urllib2.parse_http_list(s)))
{'<118>date': '2010-05-09',
 'classifier': 'Not,Spam',
 'client_name': 'example.org,[194.177.17.24]',
 'device_id': 'FE-2KA3F09000049',
 'direction': 'in',
 'disposition': 'Accept',
 'from': 'prvs=4745cd07e1=example@example.org',
 'log_id': '0400147717',
 'log_part': '00',
 'mailer': 'mta',
 'message_length': '6832079',
 'pri': 'information',
 'resolved': 'OK',
 'session_id': 'o49CedRc021772',
 'subject':'=?windows-1255?B?Rlc6IEZ3OiDg5fDp5fog+fno5fog7Pf46eHp7S3u4+Tp7SE=?=',
 'subtype': 'n/a',
 'time': '16:41:27',
 'to': 'example@example.org',
 'type': 'statistics',
 'virus': ''}
5

我不太确定你真正想要什么,不过

import re
data = "date=2010-05-09,time=16:41:27,device_id=FE-2KA3F09000049,log_id=0400147717,log_part=00,type=statistics,subtype=n/a,pri=information,session_id=o49CedRc021772,from=\"prvs=4745cd07e1=example@example.org\",mailer=\"mta\",client_name=\"example.org,[194.177.17.24]\",resolved=OK,to=\"example@example.org\",direction=\"in\",message_length=6832079,virus=\"\",disposition=\"Accept\",classifier=\"Not,Spam\",subject=\"=?windows-1255?B?Rlc6IEZ3OiDg5fDp5fog+fno5fog7Pf46eHp7S3u4+Tp7SE=?=\""
pattern = r"""(\w+)=((?:"(?:\\.|[^\\"])*"|'(?:\\.|[^\\'])*'|[^\\,"'])+)"""
print(re.findall(pattern, data))

会给你

[('date', '2010-05-09'), ('time', '16:41:27'), ('device_id', 'FE-2KA3F09000049'),
 ('log_id', '0400147717'), ('log_part', '00'), ('type', 'statistics'),
 ('subtype', 'n/a'), ('pri', 'information'), ('session_id', 'o49CedRc021772'),
 ('from', '"prvs=4745cd07e1=example@example.org"'), ('mailer', '"mta"'),
 ('client_name', '"example.org,[194.177.17.24]"'), ('resolved', 'OK'),
 ('to', '"example@example.org"'), ('direction', '"in"'),
 ('message_length', '6832079'), ('virus', '""'), ('disposition', '"Accept"'),
 ('classifier', '"Not,Spam"'), 
 ('subject', '"=?windows-1255?B?Rlc6IEZ3OiDg5fDp5fog+fno5fog7Pf46eHp7S3u4+Tp7SE=?="')
]

你可能还想在之后清理一下引号里的字符串(可以用 mystring.strip("'\""))。

编辑: 这个正则表达式现在也能正确处理被转义的引号,比如 a="She said \"Hi!\""

正则表达式的解释:

(\w+)=((?:"(?:\\.|[^\\"])*"|'(?:\\.|[^\\'])*'|[^\\,"'])+)

(\w+): 匹配一个标识符,并把它存储到第一个引用中。

=: 匹配一个 = 符号。

(: 开始捕获接下来的内容,存储到第二个引用中:

(?:: 下面的内容可以是:

"(?:\\.|[^\\"])*": 一个双引号,后面可以跟零个或多个字符,这些字符可以是转义字符或者不是引号和反斜杠的字符,最后再跟一个双引号。

|: 或者

'(?:\\.|[^\\'])*': 上面的内容,只不过是针对单引号。

|: 或者

[^\\,"']: 一个字符,不能是反斜杠、逗号或引号。

)+: 至少重复一次,尽可能多地重复。

): 结束第二个捕获组。

撰写回答