2010-05-09 5 views
6

J'ai une chaîne comme ce qui suit:chaîne pyparsing CSV avec des citations au hasard

<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="[email protected]",mailer="mta",client_name="example.org,[194.177.17.24]",resolved=OK,to="[email protected]",direction="in",message_length=6832079,virus="",disposition="Accept",classifier="Not,Spam",subject="=?windows-1255?B?Rlc6IEZ3OiDg5fDp5fog+fno5fog7Pf46eHp7S3u4+Tp7SE=?=" 

J'ai essayé en utilisant le module CSV et il ne cadrait pas, parce que je ne l'ai pas trouvé un moyen d'ignorer ce qui est cité. Pyparsing ressemblait à une meilleure réponse, mais je n'ai pas trouvé un moyen de déclarer toutes les grammaires.

Actuellement, j'utilise mon ancien script Perl pour l'analyser, mais je veux que cela soit écrit en Python. Si vous avez besoin de mon extrait Perl, je serai heureux de le fournir.

Toute aide est appréciée.

Répondre

5

Je ne sais pas ce que vous cherchez vraiment, mais

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=\"[email protected]\",mailer=\"mta\",client_name=\"example.org,[194.177.17.24]\",resolved=OK,to=\"[email protected]\",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)) 

vous donne

[('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', '"[email protected]"'), ('mailer', '"mta"'), 
('client_name', '"example.org,[194.177.17.24]"'), ('resolved', 'OK'), 
('to', '"[email protected]"'), ('direction', '"in"'), 
('message_length', '6832079'), ('virus', '""'), ('disposition', '"Accept"'), 
('classifier', '"Not,Spam"'), 
('subject', '"=?windows-1255?B?Rlc6IEZ3OiDg5fDp5fog+fno5fog7Pf46eHp7S3u4+Tp7SE=?="') 
] 

Vous pouvez nettoyer les chaînes entre guillemets après (en utilisant mystring.strip("'\"")).

EDIT: Cette regex maintenant gère correctement guillemets échappés à l'intérieur des chaînes entre guillemets (a="She said \"Hi!\"").

Explication de la regex:

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

(\w+): Faites correspondre l'identifiant et le capturer en backreference pas. 1

=: Match un =

(: saisir les éléments suivants dans backreference pas. 2:

(?:: L'un des suivants:

"(?:\\.|[^\\"])*": Un guillemet, suivi de zéro ou plusieurs des éléments suivants: un caractère échappé ou un caractère non-citation/non-barre oblique inverse, suivie d'une autre guillemet

|: ou

'(?:\\.|[^\\'])*': Voir ci-dessus, juste pour des guillemets simples.

|: ou

[^\\,"']: un caractère qui est ni une barre oblique inverse, une virgule, ni une citation.

)+: répéter au moins une fois, autant de fois que possible.

): fin du groupe de capture no. 2.

+0

Merci ce fait ce que je avais besoin. – gtfx

+0

Voici comment vous faites regex !! :) – jathanism

6

Il peut être préférable de tirer parti d'un analyseur existant plutôt que d'utiliser des regex ad-hoc.

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. 

Exemple:

>>> 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': '[email protected]', 
'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': '[email protected]', 
'type': 'statistics', 
'virus': ''} 
+0

Crédits aller à @Piotr Czapla http://stackoverflow.com/questions/1349367/parse-an-http-request-authorization-header-with-python/1349626#1349626 – jfs

+0

Ok wow, c'est en fait un vrai bijou d'une solution. Merci. – jathanism

+0

Excellent, d'autant plus qu'il supprime déjà les cotations inutiles. Eh bien, c'est la beauté de Python pour vous - piles incluses. (Bien que mon regex ne soit pas si mal non plus :)) –