【问题标题】:Pyparsing CSV string with random quotesPyparsing带有随机引号的CSV字符串
【发布时间】:2011-02-17 08:46:32
【问题描述】:

我有如下字符串:

<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 sn-p,我很乐意提供。

感谢任何帮助。

【问题讨论】:

    标签: python logging csv pyparsing


    【解决方案1】:

    利用现有的解析器可能比使用临时正则表达式更好。

    parse_http_list(s)
        按照 RFC 2068 第 2 节的描述解析列表。
    
        特别是,解析逗号分隔的列表,其中的元素
        该列表可能包含带引号的字符串。带引号的字符串可以
        包含一个逗号。不带引号的字符串可以在
        中间。如果转义,逗号和引号都不算数。
        只计算双引号,不计算单引号。
    
    parse_keqv_list(l)
        解析键不重复的键=值字符串列表。
    

    例子:

    >>> 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': ''}
    

    【讨论】:

    • 学分转到@Piotr Czapla stackoverflow.com/questions/1349367/…
    • 非常好,特别是因为它已经去掉了不必要的引号。好吧,这就是 Python 的美妙之处——包括电池。 (虽然我的正则表达式也不错:))
    • @Tim Pietzcker:你的正则表达式很好。在这种情况下,它可能会更快。
    【解决方案2】:

    我不确定你真正在寻找什么,但是

    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+):匹配标识符并将其捕获到反向引用号中。 1

    =:匹配=

    (:将以下内容捕获到反向引用编号中。 2:

    (?::以下之一:

    "(?:\\.|[^\\"])*":双引号,后跟以下零个或多个:转义字符或非引号/非反斜杠字符,后跟另一个双引号

    |: 或

    '(?:\\.|[^\\'])*':见上文,仅用于单引号。

    |: 或

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

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

    ): 捕获组编号结束。 2.

    【讨论】:

    • 谢谢你,这是我需要的。
    • 这就是你做正则表达式的方式!! :)
    猜你喜欢
    • 1970-01-01
    • 2016-03-04
    • 1970-01-01
    • 2018-04-14
    • 1970-01-01
    • 2013-01-10
    • 2016-06-08
    • 2021-05-11
    • 1970-01-01
    相关资源
    最近更新 更多